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