线程池主要目的是完成线程的复用以达到降低因为创建销毁线程的开销。来看一段代码
public class WeRun {
private final static int number = 10;
static ExecutorService service = Executors.newFixedThreadPool(number);
public static void main(String args[]) {
for(int i=0; i<number; i++) {
service.execute(new MyThread());
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0; i<number; i++) {
service.execute(new MyThread());
}
service.shutdown();
}
static class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread());
}
}
}
代码的一种执行结果为
Thread[pool-1-thread-2,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-4,5,main]
Thread[pool-1-thread-6,5,main]
Thread[pool-1-thread-7,5,main]
Thread[pool-1-thread-5,5,main]
Thread[pool-1-thread-3,5,main]
Thread[pool-1-thread-1,5,main]
Thread[pool-1-thread-8,5,main]
Thread[pool-1-thread-10,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
Thread[pool-1-thread-9,5,main]
可以看到线程池完成了线程的完美复用。那么线程池是怎样解决线程的复用了,可以看到service.execute()中每次执行的是一个新的Runnable对象。我们看看其中实现源码。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
介绍中说的是如果线程池没有满,则试着创建新的线程去运行提交的任务;如果线程池已满则放到队列中等待;如果队列已满,则执行根据拒绝策略执行。但是这似乎还是没有说清楚怎么实现线程复用的,再看看下面的内容。
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
以上代码只会在线程数目未达到上限时执行,因此线程池的线程数目最多为初始限制的最大线程数量。而新加入的Runnable任务要么直接新建线程执行,要么加入到队列中等待已有线程完成任务后从线程中取出任务执行。所以说,在线程池未销毁前,线程池中的线程不会关闭,一旦队列中有新任务则会唤醒一个线程进行任务。这就是线程的复用了!