1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.osgi.extender.internal.util.concurrent;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 /**
22 * Simple counting class which can be incremented or decremented in a
23 * synchronized manner. This class can be used as a synchronization mechanism
24 * between threads mainly though {@link #waitForZero(long)} method.
25 *
26 * The main usage of the class is to allow a master thread, to know when other
27 * threads (slaves) have passed a certain point in execution.
28 *
29 * <p/> As opposed to a Barrier or a Semaphore, this class should be used only
30 * with 1 waiting thread (a master) and any number of slave threads.
31 *
32 * <pre style="code">
33 * Thread 1:
34 * synchronized(counter) {
35 * counter.increment();
36 * thread2.start();
37 * counter.increment();
38 * thread3.start();
39 *
40 * // wait 1 second for other threads to complete
41 * counter.waitForZero(1000);
42 * }
43 *
44 * Thread 2:
45 * // do some work
46 * counter.decrement();
47 *
48 * Thread 3:
49 * // do some work
50 * counter.decrement();
51 *
52 * </pre>
53 *
54 * <p/> Mainly for usage inside the framework. All methods are thread-safe
55 * however for the master/slave pattern, synchronized blocks are recommended as
56 * multiple operations have to be executed at once.
57 *
58 * @author Costin Leau
59 *
60 */
61 public class Counter {
62
63 private int counter = 0;
64
65 private static final Log log = LogFactory.getLog(Counter.class);
66
67 private final String name;
68
69 /**
70 * Create counter with a given name.
71 * @param name counter name
72 */
73 public Counter(String name) {
74 this.name = name;
75 }
76
77 /**
78 * Increment the counter value.
79 */
80 public synchronized void increment() {
81 counter++;
82 if (log.isTraceEnabled())
83 log.trace("counter [" + name + "] incremented to " + counter);
84 }
85
86 /**
87 * Decrement the counter value.
88 */
89 public synchronized void decrement() {
90 counter--;
91 if (log.isTraceEnabled())
92 log.trace("counter [" + name + "] decremented to " + counter);
93 notifyAll();
94 }
95
96 public synchronized boolean decrementAndWait(long timeToWait) {
97 decrement();
98 if (counter > 0)
99 return waitForZero(timeToWait);
100 return true;
101 }
102
103 /**
104 * Check if the counter value is zero.
105 * @return true if value is equal or below zero, false otherwise.
106 */
107 public synchronized boolean isZero() {
108 return is(0);
109 }
110
111 public synchronized boolean is(int value) {
112 return counter == value;
113 }
114
115 /**
116 * Return the counter value.
117 *
118 * @return the counter value.
119 */
120 public synchronized int getValue() {
121 return counter;
122 }
123
124 public synchronized String toString() {
125 return "" + counter;
126 }
127
128 /**
129 * Specialized method which waits for 0. Identical to waitFor(0, waitTime).
130 *
131 * @see #waitFor(int, long)
132 * @param waitTime
133 * @return true if the waiting timed out, false otherwise
134 */
135 public synchronized boolean waitForZero(long waitTime) {
136 return waitFor(0, waitTime);
137 }
138
139 /**
140 * Wait maximum the given amount of time, for the counter to reach the given
141 * value.. This mechanism relies on {@link Object#wait(long)} and
142 * {@link Object#notify()} mechanism to work appropriately. Please see the
143 * class javadoc for more info.
144 *
145 * <p/> This method will stop waiting and return true if the thread
146 * is interrupted.
147 *
148 * @param value the value to wait for
149 * @param waitTime the time (in miliseconds) to wait for zero value
150 * @return true if the waiting timed out, false otherwise
151 */
152 public synchronized boolean waitFor(int value, long waitTime) {
153 boolean timedout = false;
154 long remainingTime = waitTime;
155 long startTime = System.currentTimeMillis();
156
157 while (counter > value && !timedout) {
158
159 try {
160 this.wait(remainingTime);
161
162 remainingTime = waitTime - (System.currentTimeMillis() - startTime);
163 timedout = remainingTime <= 0;
164 }
165 catch (InterruptedException ex) {
166 timedout = true;
167 }
168 }
169
170 return timedout;
171 }
172 }