Java线程池的架构
Executor (接口)-> ExecutorService (接口)-> AbstractExecutorService (抽
象类) -> ThreadPoolExecutor (重点关注类)
其中Executors是工具类,可以看到newCachedThreadPool与newFixedThreadPool两个方法
public static ExecutorService newCachedThreadPool() {
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>());
}
其中线程池中的阻塞队列 : SynchronousQueue, LinkedBlockingQueue, ArrayBlockingQueue都有很大的差别 (后面会说到)
ExecutorService接口
public interface ExecutorService extends Executor {
void shutdown(); //关闭线程池,已提交的任务继续执行,不接受继续提交新任务
List<Runnable> shutdownNow(); //关闭线程池中所有的任务,已提交的任务也会强制终止
boolean isShutdown(); //判断线程池是否关闭;Returns {@code true} if this executor has been shut down; 与shutdownNow()进行配合
boolean isTerminated(); // @return {@code true} if all tasks have completed following shut down;与shutdown进行配合
<T> Future<T> submit(Callable<T> task); //提交一个Callable任务
ThreadPoolExecutor 重头戏
面试重点:队列是否有界?提交任务时队列满了怎么办?什么情况下会创建新的线程?提交任务时线程池满了怎么办?空闲线程怎么关掉
核心构造函数:
1. corePoolSize 核心线程池
2. maximumPoolSize 最大线程池
3. keepAliveTime 最大存活时间 <**空闲线程的保活时间,如果某线程的空闲时间超过这个值都没有任务给他做,则该线程就可以被关闭 !!!如果线程池中的线程数少于等于核心线程数 corePoolSize,那么这些线程不会因为空闲太长时间而被关闭**>
4. 时间单位
5. 阻塞队列
6. 线程生产工厂
7. 拒绝执行处理handler
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池中的各个状态:
RUNNING:最正常的状态:接受新的任务,处理等待队列中的任务
SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务
STOP:不接受新任务提交,不再处理等待队列中的任务,中断正在执行任务的线程
TIDYING:所有的任务都销毁,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()
TERMINATED:terminated() 方法结束后,线程池的状态就会变成这个
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 当运行的线程数少于核心线程数。试图启动一个新的线程执行命令。调用addWorker并自动检查线程运行状态以及线程数,以阻止错误的线程被加
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
当到了这里 ,表明 要么线程数大于等于核心线程数 ,要么addWorker失败
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);
}
//如果workQueeu队列已满,那么进入到这个分支
//以maximumPoolSize为界创建新的worker
else if (!addWorker(command, false))
reject(command);
}
关键点分析:
ThreadPoolExecutor线程池中维护了两个关键的数据结构
- HashSet《Worker》 workers 用于存储线程
- BlockingQueue《Runnable》 workQueue用于存储任务队列
问1:*HashSet能否保证多个线程同时添加到Set集合时,不会导致集合大小维护的不准确
答1: 内部使用了一个CAS控制集合大小。
问2: new ThreadPoolExecutor(10,20,3000,TimeUnit.millsecond,new ArrayBlockingQueue(50)),请判断其任务添加时,内部状态变化的过程
答2: 当核心线程数小于10时,添加的任务直接被线程工作了;当核心线程数超过10时,任务被添加到有界阻塞队列中;当有界队列被加满时,线程数继续添加直到添加到最大值;如果还是继续添加任务,将导致线程池执行RejectedPolicy。