Annotation of java/classes/org/w3c/util/ThreadCache.java, revision 1.14
1.5 abaird 1: // ThreadCache.java
1.14 ! ylafon 2: // $Id: ThreadCache.java,v 1.13 1999/03/18 10:22:10 ylafon Exp $
1.5 abaird 3: // (c) COPYRIGHT MIT and INRIA, 1996-1997.
4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
1.11 bmahe 6: package org.w3c.util;
1.1 abaird 7:
8: class CachedThread extends Thread {
1.6 abaird 9: Runnable runner = null;
10: boolean alive = true;
11: ThreadCache cache = null;
12: CachedThread next = null;
1.14 ! ylafon 13: CachedThread prev = null;
1.2 abaird 14: boolean terminated = false;
1.8 abaird 15: boolean started = false;
1.10 abaird 16: boolean firstime = true;
1.2 abaird 17:
18: synchronized boolean isTerminated() {
19: boolean ret = terminated;
20: terminated = true;
21: return ret;
22: }
23:
1.1 abaird 24: synchronized Runnable waitForRunner() {
1.2 abaird 25: boolean to = false;
26:
1.1 abaird 27: while ( alive ) {
28: // Is a runner available ?
29: if ( runner != null ) {
30: Runnable torun = runner;
1.10 abaird 31: firstime = false;
32: runner = null;
1.1 abaird 33: return torun;
1.10 abaird 34: } else if ( firstime ) {
35: // This thread will not be declared free until it runs once:
36: try {
37: wait();
38: } catch (InterruptedException ex) {
39: }
40: } else if ( alive = cache.isFree(this, to) ) {
41: // Notify the cache that we are free, and continue if allowed:
1.1 abaird 42: try {
1.2 abaird 43: int idleto = cache.getIdleTimeout();
44: to = false;
45: if ( idleto > 0 ) {
46: wait(idleto);
1.8 abaird 47: to = (runner == null);
1.2 abaird 48: } else {
49: wait();
50: }
1.1 abaird 51: } catch (InterruptedException ex) {
52: }
53: }
54: }
55: return null;
56: }
57:
58: synchronized void kill() {
59: alive = false;
1.14 ! ylafon 60: notifyAll();
1.1 abaird 61: }
62:
1.8 abaird 63: synchronized boolean wakeup(Runnable runnable) {
64: if ( alive ) {
65: runner = runnable;
66: if ( ! started )
67: this.start();
1.14 ! ylafon 68: notifyAll();
1.8 abaird 69: return true;
70: } else {
71: return false;
72: }
1.1 abaird 73: }
74:
1.8 abaird 75: public synchronized void start() {
76: super.start();
77: this.started = true;
78: }
79:
1.1 abaird 80: public void run() {
81: try {
82: while ( true ) {
83: // Wait for a runner:
84: Runnable torun = waitForRunner();
1.8 abaird 85: // If runner, run:
86: if ( torun != null )
87: torun.run();
1.1 abaird 88: // If dead, stop
89: if ( ! alive )
90: break;
91: }
92: } finally {
93: cache.isDead(this);
94: }
95: }
96:
1.3 abaird 97: CachedThread(ThreadCache cache, int id) {
98: super(cache.getThreadGroup(), cache.getThreadGroup().getName()+":"+id);
1.1 abaird 99: this.cache = cache;
1.2 abaird 100: setPriority(cache.getThreadPriority());
101: setDaemon(true);
1.1 abaird 102: }
103:
104: }
105:
106: public class ThreadCache {
1.2 abaird 107: private static final boolean debug = false;
108:
1.1 abaird 109: /**
110: * Default number of cached threads.
111: */
112: private static final int DEFAULT_CACHESIZE = 5;
113: /**
1.2 abaird 114: * Has this thread cache been initialized ?
115: */
116: protected boolean inited = false;
117: /**
1.1 abaird 118: * The thread group for this thread cache.
119: */
120: protected ThreadGroup group = null;
121: /**
122: * Number of cached threads.
123: */
124: protected int cachesize = DEFAULT_CACHESIZE;
125: /**
126: * Number of created threads.
127: */
128: protected int threadcount = 0;
129: /**
1.3 abaird 130: * Uniq thread identifier within this ThreadCache instance.
131: */
132: protected int threadid = 0;
133: /**
1.2 abaird 134: * Number of idle threads to always maintain alive.
135: */
136: protected int idlethreads = 0;
137: /**
1.1 abaird 138: * Should we queue thread requests, rather then creating new threads.
139: */
140: protected boolean growasneeded = false;
141: /**
1.13 ylafon 142: * Number of used threads
143: */
144: protected int usedthreads = 0;
145: /**
1.1 abaird 146: * List of free threads.
147: */
148: protected CachedThread freelist = null;
1.14 ! ylafon 149: protected CachedThread freetail = null;
1.1 abaird 150: /**
1.2 abaird 151: * The idle timeout, for a thread to wait before being killed.
152: * Defaults to <strong>5000</strong> milliseconds.
153: */
154: protected int idletimeout = 5000;
155: /**
156: * Cached thread priority.
157: */
158: protected int threadpriority = 5;
159:
160: /**
161: * Get the idle timeout value for this cache.
162: * @return The idletimeout value, or negative if no timeout applies.
163: */
164:
165: synchronized final int getIdleTimeout() {
166: return (threadcount <= idlethreads) ? -1 : idletimeout;
167: }
1.1 abaird 168:
169: /**
170: * The given thread is about to be declared free.
1.4 abaird 171: * @return A boolean, <strong>true</strong> if the thread is to continue
1.1 abaird 172: * running, <strong>false</strong> if the thread should stop.
173: */
174:
1.2 abaird 175: final synchronized boolean isFree(CachedThread t, boolean timedout) {
176: if ( timedout && (threadcount > idlethreads) ) {
1.4 abaird 177: if ( ! t.isTerminated() ) {
1.2 abaird 178: threadcount--;
1.13 ylafon 179: usedthreads--;
1.4 abaird 180: notifyAll();
1.12 ylafon 181: }
1.2 abaird 182: return false;
1.12 ylafon 183: } else if ( threadcount <= cachesize ) {
1.14 ! ylafon 184: t.prev = freetail;
! 185: if (freetail != null)
! 186: freetail.next = t;
! 187: freetail = t;
! 188: if (freelist == null)
! 189: freelist = t;
1.13 ylafon 190: usedthreads--;
1.1 abaird 191: notifyAll();
192: return true;
193: } else {
1.12 ylafon 194: if ( ! t.isTerminated() ) {
1.2 abaird 195: threadcount--;
1.13 ylafon 196: usedthreads--;
1.4 abaird 197: notifyAll();
198: }
1.1 abaird 199: return false;
200: }
201: }
1.12 ylafon 202:
1.1 abaird 203: /**
204: * The given thread has terminated, cleanup any associated state.
205: * @param dead The dead CachedThread instance.
206: */
207:
208: final synchronized void isDead(CachedThread t) {
1.2 abaird 209: if ( debug )
210: System.out.println("** "+t+": is dead tc="+threadcount);
1.4 abaird 211: if ( ! t.isTerminated() ) {
1.2 abaird 212: threadcount--;
1.4 abaird 213: notifyAll();
214: }
1.1 abaird 215: }
216:
217: /**
218: * Create a new thread within this thread cache.
219: * @return A new CachedThread instance.
220: */
221:
222: private synchronized CachedThread createThread() {
223: threadcount++;
1.3 abaird 224: threadid++;
225: return new CachedThread(this, threadid);
1.1 abaird 226: }
227:
228: /**
229: * Allocate a new thread, as requested.
1.10 abaird 230: * @param waitp Should we wait until a thread is available ?
231: * @return A launched CachedThread instance, or <strong>null</strong> if
232: * unable to allocate a new thread, and <code>waitp</code> is <strong>
233: * false</strong>.
1.1 abaird 234: */
235:
236: protected synchronized CachedThread allocateThread(boolean waitp) {
237: CachedThread t = null;
238: while ( true ) {
239: if ( freelist != null ) {
1.4 abaird 240: if ( debug )
241: System.out.println("*** allocateThread: free thread");
1.1 abaird 242: t = freelist;
243: freelist = freelist.next;
1.14 ! ylafon 244: if (freelist != null) {
! 245: freelist.prev = null;
! 246: } else {
! 247: freetail = null;
! 248: }
! 249: t.next = null;
1.1 abaird 250: break;
251: } else if ((threadcount < cachesize) || growasneeded) {
1.4 abaird 252: if ( debug )
253: System.out.println("*** create new thread.");
1.1 abaird 254: t = createThread();
255: break;
256: } else if ( waitp ) {
1.4 abaird 257: if ( debug )
258: System.out.println("*** wait for a thread.");
1.1 abaird 259: // Wait for a thread to become available
260: try {
261: wait();
262: } catch (InterruptedException ex) {
263: }
264: } else {
265: return null;
266: }
267: }
268: return t;
269: }
270:
271: /**
1.2 abaird 272: * Set the thread cache size.
273: * This will also update the number of idle threads to maintain, if
274: * requested.
275: * @param cachesize The new thread cache size.
276: * @param update If <strong>true</strong> also update the number of
277: * threads to maintain idle.
278: */
279:
280: public synchronized void setCachesize(int cachesize, boolean update) {
281: this.cachesize = cachesize;
282: if ( update )
1.3 abaird 283: this.idlethreads = (cachesize>>1);
1.2 abaird 284: }
285:
286: /**
287: * Set the thread cache size.
288: * Updaet the number of idle threads to keep alive.
289: * @param cachesize The new thread cache size.
290: */
291:
292: public void setCachesize(int cachesize) {
293: setCachesize(cachesize, true);
294: }
295:
296: /**
1.5 abaird 297: * Enable/disable the thread cache to grow as needed.
298: * This flag should be turned on only if always getting a thread as fast
299: * as possible is critical.
300: * @param onoff The toggle.
301: */
302:
303: public void setGrowAsNeeded(boolean onoff) {
304: this.growasneeded = onoff;
305: }
306:
307: /**
1.2 abaird 308: * Set all the cached threads priority.
309: * Changing the cached thread priority should be done before the thread
310: * cache is initialized, it will <em>not</em> affect already created
311: * threads.
312: * @param priority The new cachewd threads priority.
313: */
314:
315: public void setThreadPriority(int priority) {
316: threadpriority = priority;
317: }
318:
319: /**
320: * Get the cached thread normal priority.
321: * @return Currently assigned cached thread priority.
322: */
323:
324: public int getThreadPriority() {
325: return threadpriority;
326: }
327:
328: /**
1.3 abaird 329: * Set the idle timeout.
330: * The idle timeout value is used to eliminate threads that have remain
331: * idle for too long (although the thread cache will ensure that a
332: * decent minimal number of threads stay around).
333: * @param idletimeout The new idle timeout.
334: */
335:
336: public synchronized void setIdleTimeout(int idletimeout) {
337: this.idletimeout = idletimeout;
338: }
339:
340: /**
1.1 abaird 341: * Request a thread to run on the given object.
342: * @param runnable The object to run with the allocated thread.
343: * @param waitp If <strong>true</strong> wait until a free thread is
344: * available, otherwise, return <strong>false</strong>.
345: * @return A boolean, <strong>true</strong> if a thread was successfully
346: * allocated for the given object, <strong>false</strong> otherwise.
347: */
348:
349: public boolean getThread(Runnable runnable, boolean waitp) {
1.4 abaird 350: if ( debug )
351: System.out.println("*** getting a thread for "+runnable);
1.2 abaird 352: if ( ! inited )
353: throw new RuntimeException("Uninitialized thread cache");
1.10 abaird 354: // Allocate and launch the thread:
1.8 abaird 355: while ( true ) {
356: CachedThread t = allocateThread(waitp);
357: if ( t != null ) {
1.13 ylafon 358: usedthreads++;
1.8 abaird 359: if ( t.wakeup(runnable) )
360: return true;
361: } else {
362: return false;
363: }
1.1 abaird 364: }
1.3 abaird 365: }
366:
367: /**
368: * Get the ThreadGroup managed by this ThreadCache instance.
369: * @return A ThreadGroup instance.
370: */
371:
372: public ThreadGroup getThreadGroup() {
373: return group;
1.13 ylafon 374: }
375:
376: /**
377: * Wait until all the threads have finished their duty
378: */
379:
380: public synchronized void waitForCompletion() {
381: while (usedthreads > 0) {
382: if ( debug )
383: System.out.println("*** Waiting for "+usedthreads+ " threads");
384: try {
385: wait();
386: } catch (InterruptedException ex) {
387: }
388: }
1.1 abaird 389: }
390:
391: /**
1.2 abaird 392: * Initialize the given thread cache.
393: * This two stage initialize method is done so that configuration
394: * of the thread cache can be done before any thread get actually
395: * created.
396: */
397:
398: public synchronized void initialize() {
1.14 ! ylafon 399: CachedThread t = createThread();
! 400: freelist = t;
! 401: freetail = t;
! 402: t.next = null;
! 403: t.prev = null;
! 404: t.start();
! 405: for (int i = 1 ; i < idlethreads ; i++) {
! 406: t = createThread();
! 407: t.next = freelist;
! 408: t.prev = null;
! 409: freelist.prev = t;
1.10 abaird 410: freelist = t;
411: t.start();
412: }
1.2 abaird 413: inited = true;
414: }
415:
416: /**
1.1 abaird 417: * Create a thread cache, whose threads are to be children of the group.
418: * @param group The thread group to which this thread cache is bound.
419: * @param nstart Number of thread to create in advance.
420: */
421:
1.2 abaird 422: public ThreadCache(ThreadGroup group) {
1.1 abaird 423: this.group = group;
424: }
425:
426: /**
427: * Create a thread cache, after creating a new thread group.
428: * @param name The name of the thread group to create.
429: */
430:
431: public ThreadCache(String name) {
1.2 abaird 432: this(new ThreadGroup(name));
1.1 abaird 433: }
434:
435: /**
436: * Create a thread cache, after creating a new thread group.
437: * @param parent The parent of the thread group to create.
438: * @param name The name of the thread group.
439: */
440:
441: public ThreadCache(ThreadGroup parent, String name) {
1.2 abaird 442: this(new ThreadGroup(parent, name));
1.1 abaird 443: }
444:
445: }
Webmaster