1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.thread;
16
17 import java.io.Serializable;
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Set;
24
25 import org.mortbay.component.AbstractLifeCycle;
26 import org.mortbay.log.Log;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 public class QueuedThreadPool extends AbstractLifeCycle implements Serializable, ThreadPool
42 {
43 private static int __id;
44
45 private String _name;
46 private Set _threads;
47 private List _idle;
48 private Runnable[] _jobs;
49 private int _nextJob;
50 private int _nextJobSlot;
51 private int _queued;
52
53 private boolean _daemon;
54 private int _id;
55
56 private final Object _threadLock = new Lock();
57 private final Object _idleLock = new Lock();
58 private final Object _jobsLock = new Lock();
59 private final Object _joinLock = new Lock();
60
61 private long _lastShrink;
62 private int _maxIdleTimeMs=60000;
63 private int _maxThreads=25;
64 private int _minThreads=2;
65 private boolean _warned=false;
66 private int _lowThreads=0;
67 private int _priority= Thread.NORM_PRIORITY;
68 private int _spawnOrShrinkAt=0;
69
70
71
72
73 public QueuedThreadPool()
74 {
75 _name="qtp"+__id++;
76 }
77
78
79
80
81 public QueuedThreadPool(int maxThreads)
82 {
83 this();
84 setMaxThreads(maxThreads);
85 }
86
87
88
89
90
91 public boolean dispatch(Runnable job)
92 {
93 if (!isRunning() || job==null)
94 return false;
95
96
97 PoolThread thread=null;
98 boolean spawn=false;
99
100
101 synchronized(_idleLock)
102 {
103 int idle=_idle.size();
104 if (idle>0)
105 thread=(PoolThread)_idle.remove(idle-1);
106 else
107 {
108
109 if (_threads.size()<_maxThreads)
110 spawn=true;
111 else
112 {
113 if (!_warned)
114 {
115 _warned=true;
116 Log.debug("Max threads for {}",this);
117 }
118 }
119 }
120 }
121
122 if (thread!=null)
123 {
124 thread.dispatch(job);
125 }
126 else
127 {
128 synchronized(_jobsLock)
129 {
130 _queued++;
131 _jobs[_nextJobSlot++]=job;
132 if (_nextJobSlot==_jobs.length)
133 _nextJobSlot=0;
134 if (_nextJobSlot==_nextJob)
135 {
136
137 Runnable[] jobs= new Runnable[_jobs.length+_maxThreads];
138 int split=_jobs.length-_nextJob;
139 if (split>0)
140 System.arraycopy(_jobs,_nextJob,jobs,0,split);
141 if (_nextJob!=0)
142 System.arraycopy(_jobs,0,jobs,split,_nextJobSlot);
143
144 _jobs=jobs;
145 _nextJob=0;
146 _nextJobSlot=_queued;
147 }
148
149 if (spawn && _queued<=_spawnOrShrinkAt)
150 spawn=false;
151 }
152
153 if (spawn)
154 newThread();
155 }
156
157
158 return true;
159 }
160
161
162
163
164
165
166 public int getIdleThreads()
167 {
168 return _idle==null?0:_idle.size();
169 }
170
171
172
173
174
175 public int getLowThreads()
176 {
177 return _lowThreads;
178 }
179
180
181
182
183
184
185
186 public int getMaxIdleTimeMs()
187 {
188 return _maxIdleTimeMs;
189 }
190
191
192
193
194
195
196
197 public int getMaxThreads()
198 {
199 return _maxThreads;
200 }
201
202
203
204
205
206
207
208 public int getMinThreads()
209 {
210 return _minThreads;
211 }
212
213
214
215
216
217 public String getName()
218 {
219 return _name;
220 }
221
222
223
224
225
226
227 public int getThreads()
228 {
229 return _threads.size();
230 }
231
232
233
234
235
236 public int getThreadsPriority()
237 {
238 return _priority;
239 }
240
241
242 public int getQueueSize()
243 {
244 return _queued;
245 }
246
247
248
249
250
251
252 public int getSpawnOrShrinkAt()
253 {
254 return _spawnOrShrinkAt;
255 }
256
257
258
259
260
261
262 public void setSpawnOrShrinkAt(int spawnOrShrinkAt)
263 {
264 _spawnOrShrinkAt=spawnOrShrinkAt;
265 }
266
267
268
269
270
271 public boolean isDaemon()
272 {
273 return _daemon;
274 }
275
276
277 public boolean isLowOnThreads()
278 {
279 return _queued>_lowThreads;
280 }
281
282
283 public void join() throws InterruptedException
284 {
285 synchronized (_joinLock)
286 {
287 while (isRunning())
288 _joinLock.wait();
289 }
290
291
292 while (isStopping())
293 Thread.sleep(100);
294 }
295
296
297
298
299
300 public void setDaemon(boolean daemon)
301 {
302 _daemon=daemon;
303 }
304
305
306
307
308
309 public void setLowThreads(int lowThreads)
310 {
311 _lowThreads = lowThreads;
312 }
313
314
315
316
317
318
319
320
321
322 public void setMaxIdleTimeMs(int maxIdleTimeMs)
323 {
324 _maxIdleTimeMs=maxIdleTimeMs;
325 }
326
327
328
329
330
331
332
333 public void setMaxThreads(int maxThreads)
334 {
335 if (isStarted() && maxThreads<_minThreads)
336 throw new IllegalArgumentException("!minThreads<maxThreads");
337 _maxThreads=maxThreads;
338 }
339
340
341
342
343
344
345
346 public void setMinThreads(int minThreads)
347 {
348 if (isStarted() && (minThreads<=0 || minThreads>_maxThreads))
349 throw new IllegalArgumentException("!0<=minThreads<maxThreads");
350 _minThreads=minThreads;
351 synchronized (_threadLock)
352 {
353 while (isStarted() && _threads.size()<_minThreads)
354 {
355 newThread();
356 }
357 }
358 }
359
360
361
362
363
364 public void setName(String name)
365 {
366 _name= name;
367 }
368
369
370
371
372
373 public void setThreadsPriority(int priority)
374 {
375 _priority=priority;
376 }
377
378
379
380
381
382 protected void doStart() throws Exception
383 {
384 if (_maxThreads<_minThreads || _minThreads<=0)
385 throw new IllegalArgumentException("!0<minThreads<maxThreads");
386
387 _threads=new HashSet();
388 _idle=new ArrayList();
389 _jobs=new Runnable[_maxThreads];
390
391 for (int i=0;i<_minThreads;i++)
392 {
393 newThread();
394 }
395 }
396
397
398
399
400
401
402
403
404
405 protected void doStop() throws Exception
406 {
407 super.doStop();
408
409 for (int i=0;i<100;i++)
410 {
411 synchronized (_threadLock)
412 {
413 Iterator iter = _threads.iterator();
414 while (iter.hasNext())
415 ((Thread)iter.next()).interrupt();
416 }
417
418 Thread.yield();
419 if (_threads.size()==0)
420 break;
421
422 try
423 {
424 Thread.sleep(i*100);
425 }
426 catch(InterruptedException e){}
427 }
428
429
430 if (_threads.size()>0)
431 Log.warn(_threads.size()+" threads could not be stopped");
432
433 synchronized (_joinLock)
434 {
435 _joinLock.notifyAll();
436 }
437 }
438
439
440 protected void newThread()
441 {
442 synchronized(_threadLock)
443 {
444 PoolThread thread =new PoolThread();
445 _threads.add(thread);
446 thread.setName(_name+"-"+_id++);
447 thread.start();
448 }
449 }
450
451
452
453
454
455
456
457
458
459 protected void stopJob(Thread thread, Object job)
460 {
461 thread.interrupt();
462 }
463
464
465
466
467
468
469
470 public class PoolThread extends Thread
471 {
472 Runnable _job=null;
473 boolean _alive=true;
474
475
476 PoolThread()
477 {
478 setDaemon(_daemon);
479 setPriority(_priority);
480 }
481
482
483
484
485
486 public void run()
487 {
488 boolean idle=false;
489 Runnable job=null;
490 try
491 {
492 while (isRunning())
493 {
494 if (job!=null)
495 {
496 final Runnable todo=job;
497 job=null;
498 idle=false;
499 todo.run();
500 }
501 else if (idle)
502 {
503
504 synchronized(_idleLock)
505 {
506 _warned=false;
507
508
509 if ((_threads.size()>_maxThreads ||
510 _idle.size()>_spawnOrShrinkAt &&
511 _threads.size()>_minThreads))
512 {
513 long now = System.currentTimeMillis();
514 if ((now-_lastShrink)>getMaxIdleTimeMs() && _idle.remove(this))
515 {
516 _lastShrink=now;
517 return;
518 }
519 }
520 }
521
522
523 try
524 {
525 synchronized (this)
526 {
527 if (_job==null)
528 this.wait(getMaxIdleTimeMs());
529 job=_job;
530 _job=null;
531 }
532 }
533 catch (InterruptedException e)
534 {
535 Log.ignore(e);
536 }
537 }
538 else
539 {
540
541 synchronized (_jobsLock)
542 {
543
544 if (_queued>0)
545 {
546 _queued--;
547 job=_jobs[_nextJob++];
548 if (_nextJob==_jobs.length)
549 _nextJob=0;
550 continue;
551 }
552 }
553
554
555 if (job==null)
556 {
557
558 synchronized (_idleLock)
559 {
560 _idle.add(this);
561 idle=true;
562 }
563 }
564 }
565 }
566 }
567 finally
568 {
569 synchronized (_idleLock)
570 {
571 _idle.remove(this);
572 }
573 synchronized (_threadLock)
574 {
575 _threads.remove(this);
576 }
577
578 synchronized (this)
579 {
580 job=_job;
581 _alive=false;
582 }
583
584 if (job!=null)
585 QueuedThreadPool.this.dispatch(job);
586 }
587 }
588
589
590 void dispatch(Runnable job)
591 {
592 synchronized (this)
593 {
594 if (_alive)
595 {
596 _job=job;
597 this.notify();
598 }
599 else
600
601 QueuedThreadPool.this.dispatch(job);
602 }
603 }
604 }
605
606 private class Lock{}
607 }