在jdk1.5后,官方为我们实现了线程池ThreadPoolExecutor,通过该线程池,我们可以一次性(其实并非一次性,而是慢慢的添加线程,直到线程池满)的预先执行代价高昂的线程分配,而且所有分配的线程都是可重用的。下面我们将会根据Executors.newFixedThreadPool为切入口分析以下线程池的创建过程和线程池的线程分配问题。
当我们执行Executors.newFixedThreadPool时,我们一直跟进构造这个方法的内部,可以发现它其实是调用了ThreadPoolExecutor的构造函数:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ThreadPoolExecutor为线程池对象,负责构建恰当的上下文来执行Runnable对象,同时负责管理所有它创建的线程的生命周期。在这个构造函数中,我们只要关注两个参数。nThreads代表的线程池的核心的线程数量(还有一个最大线程数量,这里和核心线程数量保持了一致),LinkedBlockingQueue阻塞队列对象则用来管理所有的任务并且将任务提交给工作线程(只有在当前线程池已满的情况下,才回将一个task放入阻塞队列)。
构造方法执行完成后,我们将得到一个ThreadPoolExecutor对象。注意,此时该对象不会开启任何线程。该对象中有一个重要的属性,分别如下:
1.private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
该字段代表线程池的控制状态,这个字段有两个概念上的属性:1.workercount(代表有效的线程的数量);2.runState(代表线程池的工作状态,running, shutting down etc)。
2.private final BlockingQueue<Runnable> workQueue;
用于存放task,并将task提交给工作线程(上面的阻塞队列参数注入位置)
3.private final HashSet<Worker> workers = new HashSet<Worker>();
代表所有工作任务的集合。Woker是一个继承了Runnable的类,内部有两个属性final Thread thread和Runnable firstTask,当要开启一个线程时,就会通过worker.get
thread获取该内部创建的线程(由于该thread对象创建时,worker将自身通过this作为参数传递给thread,因此开启线程后,运行的还是worker的run方法,而在run方法内部其实是调用的当前worker对象的firsttask对象)
下面我们来看看向ThreadPoolExecutor提交一个任务的时候的具体细节:
public void execute(Runnable command) {
if (command == null)