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