001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.util;
028:
029: import org.cougaar.bootstrap.SystemProperties;
030:
031: /**
032: * Provide a pool of reusable threads to reduce the overhead of
033: * construction/destruction of large numbers of standard thread,
034: * particularly on VM implementation which use OS-level thread
035: * implementations.
036: *
037: * The general contract is that wherever you might have used
038: * "new Thread()", you use "ReusableThread.newThread()" instead.
039: * Most of the Thread constructors are provided as factory methods -
040: * missing are all constructors using ThreadGroup arguments.
041: * @property org.cougaar.ReusableThread.initialPoolSize Sets the inital pool size for
042: * the thread pool utility (defaults to 32).
043: * @property org.cougaar.ReusableThread.maximumPoolSize Sets the maximum pool size for
044: * the thread pool utility (defaults to 64).
045: *
046: **/
047:
048: public class ReusableThreadPool {
049: public static ReusableThreadPool defaultPool = null;
050:
051: /** initial number of ReusableThreads in the pool **/
052: private static int defaultInitialPoolSize;
053: /** maximum number of unused ReusableThreads to keep in the pool **/
054: private static int defaultMaximumPoolSize;
055:
056: /** initialize initialPoolSize and maximumPoolSize from system,
057: * properties and create the default ThreadPool from these
058: * values.
059: */
060: static {
061: defaultInitialPoolSize = SystemProperties.getInt(
062: "org.cougaar.ReusableThread.initialPoolSize", 32);
063: defaultMaximumPoolSize = SystemProperties.getInt(
064: "org.cougaar.ReusableThread.maximumPoolSize", 64);
065: }
066:
067: /** The ThreadGroup of the pool - all threads in the pool must be
068: * members of the same threadgroup.
069: **/
070: private ThreadGroup group;
071: /** The maximum number of unused threads to keep around in the pool.
072: * anything beyond this may be destroyed or GCed.
073: **/
074: private int maximumSize;
075: /** the number of unused threads currently in the pool. **/
076: private int poolSize;
077: /** the actual pool **/
078: private ReusableThread pool[];
079:
080: public ThreadGroup getThreadGroup() {
081: return group;
082: }
083:
084: public int getMaximumSize() {
085: return maximumSize;
086: }
087:
088: public int size() {
089: return poolSize;
090: }
091:
092: private int totalThreads = 0;
093:
094: /** Return the total number of threads created. This can be larger than
095: * the maximum pool size because the pool is usually
096: * allowed to create additional threads which will not be reclaimed.
097: **/
098: public int getAllocatedThreads() {
099: return totalThreads;
100: }
101:
102: public ReusableThreadPool(ThreadGroup group, int initial,
103: int maximum) {
104: this .group = group;
105:
106: if (initial > maximum)
107: initial = maximum;
108:
109: maximumSize = maximum;
110:
111: pool = new ReusableThread[maximum];
112:
113: for (int i = 0; i < initial; i++) {
114: pool[i] = constructReusableThread();
115: }
116: poolSize = initial;
117: }
118:
119: public ReusableThreadPool(int initial, int maximum) {
120: this (Thread.currentThread().getThreadGroup(), initial, maximum);
121: }
122:
123: public ReusableThread getThread() {
124: return getThread(null, "ReusableThread");
125: }
126:
127: public ReusableThread getThread(String name) {
128: return getThread(null, name);
129: }
130:
131: public ReusableThread getThread(Runnable runnable) {
132: return getThread(runnable, "ReusableThread");
133: }
134:
135: public ReusableThread getThread(Runnable runnable, String name) {
136: ReusableThread t = null;
137:
138: synchronized (this ) {
139: if (poolSize > 0) {
140: poolSize--;
141: t = pool[poolSize];
142: pool[poolSize] = null; // clear for gc
143: }
144: }
145: if (t == null) {
146: t = constructReusableThread();
147: }
148:
149: t.setRunnable(runnable);
150: t.setName(name);
151:
152: //System.err.println("getThread pool="+poolSize+" = "+t);
153: return t;
154: }
155:
156: /** actually construct a new ReusableThread **/
157: protected ReusableThread constructReusableThread() {
158: totalThreads++;
159: return new ReusableThread(this );
160: }
161:
162: /** return a reusableThread to our pool. package protected
163: * so we don't get the wrong thread in our pool.
164: **/
165: void reclaimReusableThread(ReusableThread t) {
166: synchronized (this ) {
167: if (poolSize < maximumSize) {
168: // the pool has space - reuse it
169: pool[poolSize] = t;
170: poolSize++;
171: //System.err.println("reclaimThread pool="+poolSize+" = "+t);
172: } else {
173: // the pool is already full - drop it.
174: // t.destroy();
175: //System.err.println("reclaimThread dropped "+t);
176: }
177: }
178: }
179:
180: public static ReusableThreadPool getDefaultThreadPool() {
181: if (defaultPool == null) {
182: defaultPool = new ReusableThreadPool(
183: defaultInitialPoolSize, defaultMaximumPoolSize);
184: }
185: return defaultPool;
186: }
187:
188: //
189: // regression test
190: //
191:
192: public static class Counter {
193: private int value = 0;
194:
195: public void incr() {
196: value++;
197: }
198:
199: public String toString() {
200: return new Integer(value).toString();
201: }
202:
203: public int getValue() {
204: return value;
205: }
206:
207: public Counter() {
208: }
209:
210: public Counter(int v) {
211: value = v;
212: }
213: }
214:
215: public static class TestThread extends ReusableThread {
216: static final Counter count = new Counter();
217:
218: Counter mycount;
219: int myinvoke = 0;
220:
221: public TestThread(ReusableThreadPool p) {
222: super (p);
223: count.incr();
224: mycount = new Counter(count.getValue());
225: System.err.println("Created " + this );
226: }
227:
228: public Runnable getRunnable() {
229: myinvoke++;
230: System.err.println("Invoking " + this );
231: return super .getRunnable();
232: }
233:
234: protected void reclaim() {
235: System.err.println("Reclaiming " + this );
236: super .reclaim();
237: }
238:
239: public String toString() {
240: return "<" + getName() + " " + mycount + "(" + myinvoke
241: + ")>";
242: }
243:
244: public static int getCount() {
245: return count.getValue();
246: }
247: }
248:
249: public static class TestPool extends ReusableThreadPool {
250: public TestPool(int init, int max) {
251: super (init, max);
252: }
253:
254: protected ReusableThread constructReusableThread() {
255: ReusableThread t = new TestThread(this);
256: return t;
257: }
258: }
259: }
|