序言
有时候在项目中,频繁的创建消耗线程是一种巨大的开销,引入线程池,减少创建消耗线程的开销,让线程得到复用,提升效率。
创建线程池的几种方式
- ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler) - Executors.newFixedThreadPool(int size)
- Executors.newSingleThreadExecutor()
- Executors.newCachedThreadPool()
- Executors.newScheduledThreadPool()
- Executors.newWorkStealingPool()
执行线程池的几种方式
- void execute(Runnable command)
- Future<?> submit(Runnable task)
线程池的核心参数
- corePoolSize 核心参数,当有新任务来时,判断当前池中线程是否大于等于coreSize,如果是小于,那么新建一个线程运行任务,线程池中的核心线程创建后一般不会销毁,会常驻在池中,可以用allowCoreThreadTimeOut方法设置是否对核心线程超时销毁
- maximumPoolSize 允许的最大线程数,当一个任务来时,会先和coreSize判断,大于等于coreSize,在和workQueue判断是否已满,如果任务队列满了,在判断是否小于maximumPoolSize数,如果小于,创建新的线程执行,执行完后,当超时时间到,会销毁该线程,如果大于maximumPoolSize,会执行拒绝策略
- workQueue 任务队列,有三种:ArrayBlockingQueue(有界队列,底层数组保存,任务来了直接放入其中) LinkedBlockingQueue(无界队列,当然也可以指定大小变成有界队列,任务来了创建node封装任务,在放入队列中) SychronousQueue
- handler 当新任务来时,当前线程池线程数大于maximumPoolSize时,会执行相关策略:
new ThreadPoolExecutor.AbortPolicy() 直接抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() 在当前调用线程中直接调用任务run方法
new ThreadPoolExecutor.DiscardOldestPolicy() 抛弃队列中未处理的最老的任务,然后重试
new ThreadPoolExecutor.DiscardPolicy() 直接丢弃任务
线程池五种状态
RUNNING:接受新任务并处理排队的任务
SHUTDOWN:不接受新任务,但处理已排队的任务
STOP:不接受新任务,不处理排队的任务,中断正在进行中的任务
TIDYING:所有任务已经终止,workerCount为零,转换到状态整理的线程将运行terminated()钩子方法
TERMINATED: TERMINATED()已经完成
状态之间的转换
RUNNING -> SHUTDOWN
(RUNNING or SHUTDOWN) -> STOP
SHUTDOWN -> TIDYING
STOP -> TIDYING
TIDYING -> TERMINATED
线程的复用原理
当调用执行方法如execute时,会通过是否大于coreSize,是否加入队列,是否大于maximumPoolSize,在当coreSize时调用addWork(command, true),true表示是核心线程,如果应该加入队列,会调用workQueue.offer(command),唤醒await的线程通过notEmpty.signal()
如果是创建非核心线程:addWork(command, true)
最终在addWork中会调用start方法启动线程,最终走到ThreadPoolExecutor.Work类的runWorker(Worker w)中,while (task != null || (task = getTask()) != null)中,通过getTask()方法的Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
实现线程的复用
比如:一个核心线程来,到while循环中,第一次是task != null为true,执行完后标记false,调用task = getTask(),如果队列中有线程,并且当前线程数小于核心线程或者allowCoreThreadTimeOut是false的,那么会调用workQueue.take(),在workQueue.take()中,会判断
队列中是否有任务,如果没有notEmpty.await(),如果有弹出一个任务,并唤醒线程执行。如果是前线程数大于核心线程或者allowCoreThreadTimeOut是true,那么会调用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),如果队列中有任务,直接弹出一个任务返回执行,
如果没有任务,那么会循环等待直到超时,返回null,这时,走到while (task != null || (task = getTask()) != null),介绍该runWorker方法的调用,这样就关闭了非核心线程