View Javadoc
1   /*
2     File: Semaphore.java
3   
4     Originally written by Doug Lea and released into the public domain.
5     This may be used for any purposes whatsoever without acknowledgment.
6     Thanks for the assistance and support of Sun Microsystems Labs,
7     and everyone contributing, testing, and using this code.
8   
9     History:
10    Date       Who                What
11    11Jun1998  dl               Create public version
12     5Aug1998  dl               replaced int counters with longs
13    24Aug1999  dl               release(n): screen arguments
14  */
15  
16  package org.dbunit.util.concurrent;
17  
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  /**
22   * Base class for counting semaphores.
23   * Conceptually, a semaphore maintains a set of permits.
24   * Each acquire() blocks if necessary
25   * until a permit is available, and then takes it. 
26   * Each release adds a permit. However, no actual permit objects
27   * are used; the Semaphore just keeps a count of the number
28   * available and acts accordingly.
29   * <p>
30   * A semaphore initialized to 1 can serve as a mutual exclusion
31   * lock. 
32   * <p>
33   * Different implementation subclasses may provide different
34   * ordering guarantees (or lack thereof) surrounding which
35   * threads will be resumed upon a signal.
36   * <p>
37   * The default implementation makes NO 
38   * guarantees about the order in which threads will 
39   * acquire permits. It is often faster than other implementations.
40   * <p>
41   * <b>Sample usage.</b> Here is a class that uses a semaphore to
42   * help manage access to a pool of items.
43   * <pre>
44   * class Pool {
45   *   static final MAX_AVAILABLE = 100;
46   *   private final Semaphore available = new Semaphore(MAX_AVAILABLE);
47   *   
48   *   public Object getItem() throws InterruptedException { // no synch
49   *     available.acquire();
50   *     return getNextAvailableItem();
51   *   }
52   *
53   *   public void putItem(Object x) { // no synch
54   *     if (markAsUnused(x))
55   *       available.release();
56   *   }
57   *
58   *   // Not a particularly efficient data structure; just for demo
59   *
60   *   protected Object[] items = ... whatever kinds of items being managed
61   *   protected boolean[] used = new boolean[MAX_AVAILABLE];
62   *
63   *   protected synchronized Object getNextAvailableItem() { 
64   *     for (int i = 0; i < MAX_AVAILABLE; ++i) {
65   *       if (!used[i]) {
66   *          used[i] = true;
67   *          return items[i];
68   *       }
69   *     }
70   *     return null; // not reached 
71   *   }
72   *
73   *   protected synchronized boolean markAsUnused(Object item) { 
74   *     for (int i = 0; i < MAX_AVAILABLE; ++i) {
75   *       if (item == items[i]) {
76   *          if (used[i]) {
77   *            used[i] = false;
78   *            return true;
79   *          }
80   *          else
81   *            return false;
82   *       }
83   *     }
84   *     return false;
85   *   }
86   *
87   * }
88   *</pre>
89   * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
90   * 
91   * @author Doug Lea
92   * @author Last changed by: $Author$
93   * @version $Revision$ $Date$
94   * @since ? (pre 2.1)
95   */
96  public class Semaphore implements Sync  {
97  
98      /**
99       * Logger for this class
100      */
101     private static final Logger logger = LoggerFactory.getLogger(Semaphore.class);
102 
103   /** current number of available permits **/
104   protected long permits_;
105 
106   /** 
107    * Create a Semaphore with the given initial number of permits.
108    * Using a seed of one makes the semaphore act as a mutual exclusion lock.
109    * Negative seeds are also allowed, in which case no acquires will proceed
110    * until the number of releases has pushed the number of permits past 0.
111   **/
112   public Semaphore(long initialPermits) {  permits_ = initialPermits; }
113 
114 
115   /** Wait until a permit is available, and take one **/
116   public void acquire() throws InterruptedException {
117         logger.debug("acquire() - start");
118 
119     if (Thread.interrupted()) throw new InterruptedException();
120     synchronized(this) {
121       try {
122         while (permits_ <= 0) wait();
123         --permits_;
124       }
125       catch (InterruptedException ex) {
126         notify();
127         throw ex;
128       }
129     }
130   }
131 
132   /** Wait at most msecs millisconds for a permit. **/
133   public boolean attempt(long msecs) throws InterruptedException {
134         logger.debug("attempt(msecs={}) - start", String.valueOf(msecs));
135 
136     if (Thread.interrupted()) throw new InterruptedException();
137 
138     synchronized(this) {
139       if (permits_ > 0) { 
140         --permits_;
141         return true;
142       }
143       else if (msecs <= 0)   
144         return false;
145       else {
146         try {
147           long startTime = System.currentTimeMillis();
148           long waitTime = msecs;
149           
150           for (;;) {
151             wait(waitTime);
152             if (permits_ > 0) {
153               --permits_;
154               return true;
155             }
156             else { 
157               waitTime = msecs - (System.currentTimeMillis() - startTime);
158               if (waitTime <= 0) 
159                 return false;
160             }
161           }
162         }
163         catch(InterruptedException ex) {
164           notify();
165           throw ex;
166         }
167       }
168     }
169   }
170 
171   /** Release a permit **/
172   public synchronized void release() {
173         logger.debug("release() - start");
174 
175     ++permits_;
176     notify();
177   }
178 
179 
180   /** 
181    * Release N permits. <code>release(n)</code> is
182    * equivalent in effect to:
183    * <pre>
184    *   for (int i = 0; i < n; ++i) release();
185    * </pre>
186    * <p>
187    * But may be more efficient in some semaphore implementations.
188    * @exception IllegalArgumentException if n is negative.
189    **/
190   public synchronized void release(long n) {
191         logger.debug("release(n={}) - start", String.valueOf(n));
192 
193     if (n < 0) throw new IllegalArgumentException("Negative argument");
194 
195     permits_ += n;
196     for (long i = 0; i < n; ++i) notify();
197   }
198 
199   /**
200    * Return the current number of available permits.
201    * Returns an accurate, but possibly unstable value,
202    * that may change immediately after returning.
203    **/
204   public synchronized long permits() {
205     return permits_;
206   }
207 
208 }
209