View Javadoc

1   /*
2    * Copyright 2006-2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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 			// start waiting
159 			try {
160 				this.wait(remainingTime);
161 				// compute the remaining time
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 }