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