Java线程池
线程池的流程如下:
- 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
- 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
- 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程(非核心线程池)来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
实际上线程池分为两种线程,一种是核心线程池,另一种是非核心线程。核心线程数会一直在线程池中存活,即使处于闲置状态。非核心线程闲置时的超时时长超过keepAliveTime,则非核心线程就会被回收。
以下图来自Java并发编程的艺术,这两张图已经很好解释了线程池的主要处理流程和ThreadPoolExecutor执行流程
ThreadPoolExecutor详解
Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,由四个组件构成
- corePool: 核心线程池的大小
- maximumPool: 最大线程池的大小
- BlockingQueue:用来暂时保存任务的工作队列
- RejectedExecutionHandler: 当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时,execute()方法将要调用的Handler。
1. FixThreadPool
FixThreadPool被称为可重用固定线程数的线程池,应用于需要限制当前线程数量的应用场景,适用于负载比较重的服务器。
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。
当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize。使用无界队列时maximumPoolSize和keepAliveTime将是一个无效参数
2. SingleThreadExecutor
SingleThreadExecutor在线程池中有且只有一个核心线程,适用于需要保证顺序地执行各个任务。
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
corePoolSize和maximumPoolSize都被设置为1,其余参数与FixedTheadPool相同。
3. CachedThreadPool
CachedThreadPool是一个会根据需要创建新线程的线程池
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
}
corePoolSize设置为0,即核心线程为空,表明使用的都是非核心线程,maximumPoolSize设置为Integer.MAX_VALUE,即表示无界,keepAliveTime设置为60s,表示非核心线程等待新任务的最长时间为60s,超过60s,非核心线程将会被终止。SynchronousQueue是一个没有容量的阻塞队列,每个插入操作必须等待另一个线程的对应得移除操作,否则插入队列失败。
CachedThreadPool执行流程如下:
- 首先执行SynchronousQueue的插入,如果当前maximumPool中有非核心线程正在执行SynchronousQueue的移除,那么主线程执行的插入操作和非核心线程执行的移除操作配对成功,主线程把任务交给非核心线程执行,否则执行步骤2
- 当初始maximumPool为空,或者maximumPool中没有非核心线程时,无法执行SynchronousQueue的移除操作,这种情况下插入队列失败,此时CachedTheadPool会创建一个新线程执行任务。
- 在步骤2中创建的线程将任务执行完后,会执行SynchronousQueue的移除操作,这个移除操作会让非核心线程最多在SynchronousQueue中等待60s,如果60s内主线程提交了一个新任务,那么这个非核心线程将执行主线程提交的新任务,否则这个非核心线程将被终止回收。