java线程池原理
Executors类提供了FixedThreadPool、CachedThreadPool等线程池对象,都是基于ThreadPoolExecutor产生的。
Executor接口定义了execute(),子接口ExecutorService又定义了submit(),抽象类AbstractExecutorService继承了该子接口,调用execute()实现了submit(),ThreadPoolExecutor继承AbstractExecutorService接口,实现了execute()。
ThreadPoolExecutor主要持有一个HashSet用于存储worker,持有一个阻塞队列用于存储待执行的任务(Runnable/Callable)。如果HashSet未达到指定大小,接到任务时立即创建worker,否则将任务存入阻塞队列。创建worker时启动一个线程,构造worker时传入的第一个Runnale开始执行,执行完后通过getTask()从阻塞队列中取出任务继续执行。
public class ThreadPoolExecutor extends AbstractExecutorService {
//持有阻塞队列和HashSet,分别存放排队的任务和已有的worker。
private final BlockingQueue<Runnable> workQueue;
private final HashSet<Worker> workers = new HashSet<Worker>();
...
private boolean addWorker(Runnable firstTask, boolean core) {
Worker w = new Worker(firstTask);
Thread t = w.thread;
workers.add(w);
//启动worker持有的线程。
t.start();
}
...
final void runWorker(Worker w) {
//让worker开始执行任务
Runnable task = w.firstTask;
//第一次执行firstTask,之后循环从队列中取任务执行
while (task != null || (task = getTask()) != null) {
task.run();
}
}
...
private Runnable getTask() {
//此处决定了是一直阻塞还是超时结束
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
}
...
private final class Worker implements Runnable{
//注意worker本身也是一个Runnable
Worker(Runnable firstTask) {
//创建worker时就创建了新线程,同时持有了第一个任务,但此时线程没有执行!
this.firstTask = firstTask;
//注意该线程传入的是worker自身,start()后运行的是下面的run(),
this.thread = getThreadFactory().newThread(this);
}
public void run() {
//在worker的线程中执行外部类的runWorker(Worker w)方法
runWorker(this);
}
}
}
实现细节:worker本身接收一个Runnable任务,同时持有一个线程(该线程包含的Runnable就是worker自己!),线程池新建worker后调用worker.thread.start()开始子线程,执行worker的run(),在子线程中调用外部类的runWorker()方法(worker作为内部类的对象可以调用外部类的方法),从外部的HashSet中读取任务,从而实现在子线程中运行外部类队列中任务的目的!!同时在HashSet中保存worker的引用,便于对子线程进行管理。
FixedThreadPool、CachedThreadPool的区别主要在于HashSet的大小限制和getTask()时表现不同,FixedThreadPool的HashSet大小由参数决定,同时运行的线程数有限,同时getTask()时使用的是阻塞队列的take()方法,无任务时一致阻塞,线程不会结束。CachedThreadPool的HashSet大小为Integer.MAX_VALUE,可以无限增加,getTask()时使用的是poll(),阻塞队列的该方法在延时后返回null,所以该线程池的线程一段时间不用后会自动结束。
public class ThreadPoolExecutor{
//ThreadPoolExecutor的通用线程策略是:
//1、线程数小于核心线程数时,尝试创建线程
//2、达到核心线程数时,尝试放入任务队列
//3、放入失败时开始第二次新建线程阶段(CachedThreadPool就是利用了这一点),大于最大线程数时就报错。
...
//CachedThreadPool与FixedThreadPool在无任务时线程是否结束上的区别就在于此
private Runnable getTask(){
...
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
...
}
...
}
//线程数上限Integer.MAX_VALUE,60秒无任务就结束
public static ExecutorService newCachedThreadPool() {
//默认60秒无任务就结束
//核心线程数为0,即一有任务立即进入第二阶段尝试放入任务队列
//而SynchronousQueue性质是若有线程等待则放入操作会成功,无任务等待时放入操作失败,线程池自动进入第二次新建线程阶段。
//从而实现了复用空闲线程,无空闲线程则自动新建的功能。
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//固定大小线程池,无任务时一直等待,不结束
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
另外两个常见的线程池SingleThreadExecutor、ScheduledThreadPool
//单线程池SingleThreadExecutor就是newFixedThreadPool(1)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>())
);
}
//延时调度线程池,对于池中的每个线程,runnable任务可以设置延时执行,且可以设为隔段时间重复执行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
//延时0秒,每隔3秒重复执行一次
//scheduleAtFixedRate 下次任务开始-本次任务开始=3秒
//scheduleWithFixedRate 下次任务开始-本次任务结束=3秒
threadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("3");
}
},0,3, TimeUnit.SECONDS);