背景
再现代的操作系统中,进程是资源分配的最小单位,而线程是处理器调度的最小单位,而再Java语言中,最小调度代码的单位也是线程(Thread),在越来越多的高并发,要求实时性高的系统中多线程对对单线程来说有着很大的优势.
为什么要用线程池
在Java中,我们在开启一个线程并运行的时候,会这样写 new Thread(() ->{}).start();
,
如果要开启多个线程,那么我们要这样写:
new Thread(() ->{});
new Thread(() ->{});
new Thread(() ->{});
new Thread(() ->{});
.....
这样的写法不但又代码冗余,不美观之外,还会带来其他问题:
首先,我们来计算一个线程运行的时间:
开启线程所用时间(T1)+
线程正真执行代码所用的时间(T2)+
关闭线程所用过的时间(T3)
问题:
1:如果T1+T3>T2,我们要花比线程执行时间还多的时间用来开启,关闭线程
2.如果线程不够还要重写代码开启更多的线程
3.如果开启的线程过多,无法回收,空着占用系统资源
线程池
线程池,数据库连接池等 “池”,都是提前把资源准备好,等到用的时候直接用,不用再创建,用完之后,再放回”池”子里面,接下来,对照着源码来学习Java中的线程池
Executor
这是java1.5的时候新加入的一个接口,是大名鼎鼎的Doug Lea写的,这个接口中只定义了一个方法,入参是Runnable的一个实例,其实就是执行run()方法
/*
* @since 1.5
* @author Doug Lea
*/
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
ExecutorService
解释几个重要的方法
此方法不等待先前提交的任务完成执行,也就是直接会停止当前提交的任务
void shutdown();
此方法会会立马终止当前执行的线程,返回没有执行的任务列表
List<Runnable> shutdownNow();
等待当前所有任务执行完,或者没有在才规定的时间内执行完,会停止
boolean awaitTermination(long timeout, TimeUnit unit)
此方法会传入一个回调类型,在提交任务之后,会异步返回结果
<T> Future<T> submit(Callable<T> task);
ThreadPoolExecutor
这个类是ExecutorService默认的实现类
线程池状态:
RUNNING:接受新的task,并且执行队列中已有的task
SHUTDOWN:不再接受新的task,但是执行完队列中已又的task
STOP:不再接受新的task,并且不再执行队列中的task,并且中断正在执行的task
TIDYING :所有任务都已终止,workerCount为零, 线程转换到状态TIDYING,将运行terminate()回调方法
TERMINATED:terminated()方法执行完毕,将状态改为TERMINATED
以下为状态互相转换的几种情况
RUNNING -> SHUTDOWN
On invocation of shutdown(), perhaps implicitly in finalize()
(RUNNING or SHUTDOWN) -> STOP
On invocation of shutdownNow()
SHUTDOWN -> TIDYING
When both queue and pool are empty
STOP -> TIDYING
When pool is empty
TIDYING -> TERMINATED
When the terminated() hook method has completed
其他重要属性
用来存放工作集:
private final BlockingQueue<Runnable> workQueue;
线程池主要状态,等改变的时候都要用的可重入锁
private final ReentrantLock mainLock = new ReentrantLock();
线程池中工作集,只有在得到锁的时候,才可以更改
private final HashSet<Worker> workers = new HashSet<>();
执行execute()方法遵循的三个步骤
1.如果当前线程数量小于corePoolSize则新建线程,用来执行task
2.如果当前线程大于corePoolSize,则将它加入等待队列,等待被执行, 如果添加失败,则新建线程去执行
3.如果当前线程数量大于maximumPoolSize,则执行拒绝策略
ThreadPoolExecutor线程池拒绝策略:
1.ThreadPoolExecutor.AbortPolicy:当线程数量大于maximumPoolSize时候,拒绝接收task,并抛出一个RejectedExecutionException异常
2. DiscardPolicy:当线程数量大于maximumPoolSize时候,拒绝接受task,并静默丢弃task
3. DiscardOldestPolicy:丢弃最久未使用的task,接受新的task
4. CallerRunsPolicy:不加入队列,由调用线程处理该任务
任务缓存队列的设置策略
1.ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2.LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
3.SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
4.PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
ThreadPoolExecutor构造器参数解释:
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
int corePoolSize:顾名思义,线程池中同时执行几个线程的大小
int maximumPoolSize:线程池最大的大小
long keepAliveTime:当线程池中的线程数大于corePoolSize的时候,开始执行timeUnit后在线程消亡前最大存活时间
ThreadFactory threadFactory:用线程工厂生产
RejectedExecutionHandler handler:如果缓冲队列满了,拒绝策略