View Javadoc

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