001: // ThreadCache.java
002: // $Id: ThreadCache.java,v 1.16 2000/08/16 21:37:58 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996-1997.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.util;
007:
008: class CachedThread extends Thread {
009: Runnable runner = null;
010: boolean alive = true;
011: ThreadCache cache = null;
012: CachedThread next = null;
013: CachedThread prev = null;
014: boolean terminated = false;
015: boolean started = false;
016: boolean firstime = true;
017:
018: synchronized boolean isTerminated() {
019: boolean ret = terminated;
020: terminated = true;
021: return ret;
022: }
023:
024: synchronized Runnable waitForRunner() {
025: boolean to = false;
026:
027: while (alive) {
028: // Is a runner available ?
029: if (runner != null) {
030: Runnable torun = runner;
031: firstime = false;
032: runner = null;
033: return torun;
034: } else if (firstime) {
035: // This thread will not be declared free until it runs once:
036: try {
037: wait();
038: } catch (InterruptedException ex) {
039: }
040: } else if (alive = cache.isFree(this , to)) {
041: // Notify the cache that we are free, and continue if allowed:
042: try {
043: int idleto = cache.getIdleTimeout();
044: to = false;
045: if (idleto > 0) {
046: wait(idleto);
047: to = (runner == null);
048: } else {
049: wait();
050: }
051: } catch (InterruptedException ex) {
052: }
053: }
054: }
055: return null;
056: }
057:
058: synchronized void kill() {
059: alive = false;
060: notify();
061: }
062:
063: synchronized boolean wakeup(Runnable runnable) {
064: if (alive) {
065: runner = runnable;
066: if (!started)
067: this .start();
068: notify();
069: return true;
070: } else {
071: return false;
072: }
073: }
074:
075: public synchronized void start() {
076: super .start();
077: this .started = true;
078: }
079:
080: public void run() {
081: try {
082: while (true) {
083: // Wait for a runner:
084: Runnable torun = waitForRunner();
085: // If runner, run:
086: if (torun != null)
087: torun.run();
088: // If dead, stop
089: if (!alive)
090: break;
091: }
092: } finally {
093: cache.isDead(this );
094: }
095: }
096:
097: CachedThread(ThreadCache cache, int id) {
098: super (cache.getThreadGroup(), cache.getThreadGroup().getName()
099: + ":" + id);
100: this .cache = cache;
101: setPriority(cache.getThreadPriority());
102: setDaemon(true);
103: }
104:
105: }
106:
107: public class ThreadCache {
108: private static final boolean debug = false;
109:
110: /**
111: * Default number of cached threads.
112: */
113: private static final int DEFAULT_CACHESIZE = 5;
114: /**
115: * Has this thread cache been initialized ?
116: */
117: protected boolean inited = false;
118: /**
119: * The thread group for this thread cache.
120: */
121: protected ThreadGroup group = null;
122: /**
123: * Number of cached threads.
124: */
125: protected int cachesize = DEFAULT_CACHESIZE;
126: /**
127: * Number of created threads.
128: */
129: protected int threadcount = 0;
130: /**
131: * Uniq thread identifier within this ThreadCache instance.
132: */
133: protected int threadid = 0;
134: /**
135: * Number of idle threads to always maintain alive.
136: */
137: protected int idlethreads = 0;
138: /**
139: * Should we queue thread requests, rather then creating new threads.
140: */
141: protected boolean growasneeded = false;
142: /**
143: * Number of used threads
144: */
145: protected int usedthreads = 0;
146: /**
147: * List of free threads.
148: */
149: protected CachedThread freelist = null;
150: protected CachedThread freetail = null;
151: /**
152: * The idle timeout, for a thread to wait before being killed.
153: * Defaults to <strong>5000</strong> milliseconds.
154: */
155: protected int idletimeout = 5000;
156: /**
157: * Cached thread priority.
158: */
159: protected int threadpriority = 5;
160:
161: /**
162: * Get the idle timeout value for this cache.
163: * @return The idletimeout value, or negative if no timeout applies.
164: */
165:
166: synchronized final int getIdleTimeout() {
167: return (threadcount <= idlethreads) ? -1 : idletimeout;
168: }
169:
170: /**
171: * The given thread is about to be declared free.
172: * @return A boolean, <strong>true</strong> if the thread is to continue
173: * running, <strong>false</strong> if the thread should stop.
174: */
175:
176: final synchronized boolean isFree(CachedThread t, boolean timedout) {
177: if (timedout && (threadcount > idlethreads)) {
178: if (!t.isTerminated()) {
179: threadcount--;
180: usedthreads--;
181: notifyAll();
182: }
183: return false;
184: } else if (threadcount <= cachesize) {
185: t.prev = freetail;
186: if (freetail != null)
187: freetail.next = t;
188: freetail = t;
189: if (freelist == null)
190: freelist = t;
191: usedthreads--;
192: notifyAll();
193: return true;
194: } else {
195: if (!t.isTerminated()) {
196: threadcount--;
197: usedthreads--;
198: notifyAll();
199: }
200: return false;
201: }
202: }
203:
204: /**
205: * The given thread has terminated, cleanup any associated state.
206: * @param dead The dead CachedThread instance.
207: */
208:
209: final synchronized void isDead(CachedThread t) {
210: if (debug)
211: System.out.println("** " + t + ": is dead tc="
212: + threadcount);
213: if (!t.isTerminated()) {
214: threadcount--;
215: notifyAll();
216: }
217: }
218:
219: /**
220: * Create a new thread within this thread cache.
221: * @return A new CachedThread instance.
222: */
223:
224: private synchronized CachedThread createThread() {
225: threadcount++;
226: threadid++;
227: return new CachedThread(this , threadid);
228: }
229:
230: /**
231: * Allocate a new thread, as requested.
232: * @param waitp Should we wait until a thread is available ?
233: * @return A launched CachedThread instance, or <strong>null</strong> if
234: * unable to allocate a new thread, and <code>waitp</code> is <strong>
235: * false</strong>.
236: */
237:
238: protected synchronized CachedThread allocateThread(boolean waitp) {
239: CachedThread t = null;
240: while (true) {
241: if (freelist != null) {
242: if (debug)
243: System.out
244: .println("*** allocateThread: free thread");
245: t = freelist;
246: freelist = freelist.next;
247: if (freelist != null) {
248: freelist.prev = null;
249: } else {
250: freetail = null;
251: }
252: t.next = null;
253: break;
254: } else if ((threadcount < cachesize) || growasneeded) {
255: if (debug)
256: System.out.println("*** create new thread.");
257: t = createThread();
258: break;
259: } else if (waitp) {
260: if (debug)
261: System.out.println("*** wait for a thread.");
262: // Wait for a thread to become available
263: try {
264: wait();
265: } catch (InterruptedException ex) {
266: }
267: } else {
268: return null;
269: }
270: }
271: return t;
272: }
273:
274: /**
275: * Set the thread cache size.
276: * This will also update the number of idle threads to maintain, if
277: * requested.
278: * @param cachesize The new thread cache size.
279: * @param update If <strong>true</strong> also update the number of
280: * threads to maintain idle.
281: */
282:
283: public synchronized void setCachesize(int cachesize, boolean update) {
284: this .cachesize = cachesize;
285: if (update)
286: this .idlethreads = (cachesize >> 1);
287: }
288:
289: /**
290: * Set the thread cache size.
291: * Updaet the number of idle threads to keep alive.
292: * @param cachesize The new thread cache size.
293: */
294:
295: public void setCachesize(int cachesize) {
296: setCachesize(cachesize, true);
297: }
298:
299: /**
300: * Enable/disable the thread cache to grow as needed.
301: * This flag should be turned on only if always getting a thread as fast
302: * as possible is critical.
303: * @param onoff The toggle.
304: */
305:
306: public void setGrowAsNeeded(boolean onoff) {
307: this .growasneeded = onoff;
308: }
309:
310: /**
311: * Set all the cached threads priority.
312: * Changing the cached thread priority should be done before the thread
313: * cache is initialized, it will <em>not</em> affect already created
314: * threads.
315: * @param priority The new cachewd threads priority.
316: */
317:
318: public void setThreadPriority(int priority) {
319: threadpriority = priority;
320: }
321:
322: /**
323: * Get the cached thread normal priority.
324: * @return Currently assigned cached thread priority.
325: */
326:
327: public int getThreadPriority() {
328: return threadpriority;
329: }
330:
331: /**
332: * Set the idle timeout.
333: * The idle timeout value is used to eliminate threads that have remain
334: * idle for too long (although the thread cache will ensure that a
335: * decent minimal number of threads stay around).
336: * @param idletimeout The new idle timeout.
337: */
338:
339: public synchronized void setIdleTimeout(int idletimeout) {
340: this .idletimeout = idletimeout;
341: }
342:
343: /**
344: * Request a thread to run on the given object.
345: * @param runnable The object to run with the allocated thread.
346: * @param waitp If <strong>true</strong> wait until a free thread is
347: * available, otherwise, return <strong>false</strong>.
348: * @return A boolean, <strong>true</strong> if a thread was successfully
349: * allocated for the given object, <strong>false</strong> otherwise.
350: */
351:
352: public boolean getThread(Runnable runnable, boolean waitp) {
353: if (debug)
354: System.out.println("*** getting a thread for " + runnable);
355: if (!inited)
356: throw new RuntimeException("Uninitialized thread cache");
357: // Allocate and launch the thread:
358: while (true) {
359: CachedThread t = allocateThread(waitp);
360: if (t != null) {
361: if (t.wakeup(runnable)) {
362: synchronized (this ) {
363: usedthreads++;
364: }
365: return true;
366: }
367: } else {
368: return false;
369: }
370: }
371: }
372:
373: /**
374: * Get the ThreadGroup managed by this ThreadCache instance.
375: * @return A ThreadGroup instance.
376: */
377:
378: public ThreadGroup getThreadGroup() {
379: return group;
380: }
381:
382: /**
383: * Wait until all the threads have finished their duty
384: */
385:
386: public synchronized void waitForCompletion() {
387: while (usedthreads > 0) {
388: if (debug)
389: System.out.println("*** Waiting for " + usedthreads
390: + " threads");
391: try {
392: wait();
393: } catch (InterruptedException ex) {
394: }
395: }
396: }
397:
398: /**
399: * Initialize the given thread cache.
400: * This two stage initialize method is done so that configuration
401: * of the thread cache can be done before any thread get actually
402: * created.
403: */
404:
405: public synchronized void initialize() {
406: CachedThread t = createThread();
407: freelist = t;
408: freetail = t;
409: t.next = null;
410: t.prev = null;
411: t.start();
412: for (int i = 1; i < idlethreads; i++) {
413: t = createThread();
414: t.next = freelist;
415: t.prev = null;
416: freelist.prev = t;
417: freelist = t;
418: t.start();
419: }
420: inited = true;
421: }
422:
423: /**
424: * Create a thread cache, whose threads are to be children of the group.
425: * @param group The thread group to which this thread cache is bound.
426: * @param nstart Number of thread to create in advance.
427: */
428:
429: public ThreadCache(ThreadGroup group) {
430: this .group = group;
431: }
432:
433: /**
434: * Create a thread cache, after creating a new thread group.
435: * @param name The name of the thread group to create.
436: */
437:
438: public ThreadCache(String name) {
439: this (new ThreadGroup(name));
440: }
441:
442: /**
443: * Create a thread cache, after creating a new thread group.
444: * @param parent The parent of the thread group to create.
445: * @param name The name of the thread group.
446: */
447:
448: public ThreadCache(ThreadGroup parent, String name) {
449: this (new ThreadGroup(parent, name));
450: }
451:
452: }
|