线程池优点
- 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
- 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
- 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
线程池核心设计与实现
线程池的总体设计
- Executor是顶层接口,提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。
- ExecutorService接口增加了一些能力:
1.扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;
2.提供了管控线程池的方法,比如停止线程池的运行。 - AbstractExecutorService是ThreadPoolExecutor的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。
- Java中的线程池核心实现类是ThreadPoolExecutor,ThreadPoolExecutor 将会一方面维护自身的生命周期,同时管理线程和任务,使两者良好的结合从而执行并行任务。
ThreadPoolExecutor的运行
线程池的运行主要分成两部分:任务管理、线程管理(内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦)。
任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:
(1)直接申请线程执行该任务;
(2)缓冲到队列中等待线程执行;
(3)拒绝该任务。
生命周期管理
线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。
线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount)。
在具体实现中,线程池将运行状态(runState)、线程数量 (workerCount)两个关键参数的维护放在了一起。
ThreadPoolExecutor 运行的5种状态
Running | 能接受新提交的任务,并且也能处理阻塞队列里的任务。 |
---|---|
Shutdown | 关闭状态,不再接受新提交的任务,但可以继续处理阻塞队列里的任务。 |
Stop | 不能接受新任务,也不能处理队列里的任务,会中断正在处理任务的线程。 |
Tidying | 所有任务都终止,workerCount(有效线程数)为0. |
Terminated | 在terminated()方法执行后进入此状态。 |
ThreadPoolExecutor的生命周期转换
任务执行机制
任务调度
首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
1.如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
2.如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
3.如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
4.如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
任务缓冲
阻塞队列(BlockingQueue),在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
不同的队列可以实现不一样的任务存取策略
ArrayBlockingQueue | 数组实现的有界阻塞队列,按照FIFO原则对元素排序,支持公平锁和非公平锁。 |
---|---|
LikedBlockingQueue | 链表结构组成的有界队列,按照FIFO原则对元素排序,默认长度为Integer.MAX_VALUE。 |
PrioriBlockQueue | 支持线程优先级排序的无界队列,默认自然排序,也可实现compareTo()方法排序。 |
DelayQueue | 延时获取的无界队列,创建元素时可指定多久后才可以从队列中获取元素。 |
SynchronousQueue | 不储存元素,每次put必须等待take,否则不能添加元素,支持公平锁和非公平锁。 |
LinkedTransferQueue | 链表构成的无界队列,多了transfer()、tryTransfer()在队尾插元素方法。 |
LinkedBlockingDeque | 链表构成的双向队列,头尾都可添加移除元素,可降低锁的竞争。 |
任务申请
(1)一种是任务直接由新创建的线程执行。
(2)是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行,线程从任务缓存中不断地取任务执行。这部分策略由getTask方法实现。
任务拒绝
四种拒绝策略
ThreadPoolExecutor.AbortPolicy | 线程池默认1拒绝策略,丢弃任务并抛出RejectedExecutionException异常(推荐使用)。 |
---|---|
ThreadPoolExecutor.DiscardPolicy | 丢弃任务,不抛异常。 |
ThreadPoolExecutor.DiscardOldestPolicy | 丢弃队列前面的任务,重新提交被拒绝的任务。 |
ThreadPoolExecutor.CallerRunnsPolicy | 由调用线程(提交任务的线程)处理该任务,让每个任务都执行完毕。 |
ThreadPoolExecutor
ThreadPoolExecutor是创建线程池最传统和最推荐使用的方式,创建时要设置线程池的核心线程数和最大线程数还有任务队列集合。
线程池参数
corePoolSize | 线程池核心线程数,没有任务时,核心线程也会存活。设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。 |
---|---|
maximumPoolSize | 线程池最大数。当线程数>= corePoolSize ,且任务队列已满时。线程池会创建新线程来处理任务,当线程数= maximumPoolSize ,且任务队列已满时,线程池会拒绝处理任务而抛出异常。 |
keepAliveTime | 空闲线程存活时间,当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize。如果 allowCoreThreadTimeout=true,则会直到线程数量=0。 |
unit | 时间单位。 |
workQueue | 线程池所使用的缓冲队列,当核心线程数达到最大时,新任务会放在队列中排队等待执行。 |
handler | 线程池对拒绝任务的处理策略。 |
ThreadFactory | 负责生产线程的线程工厂。是一个接口类,而且方法只有一个。就是创建一个线程。为了统一在创建线程时设置一些参数,是否守护线程,优先级等。通过这个 TreadFactory 创建出来的线程能保证有相同的特性。 |
ThreadPoolExecutor的一些方法
submit()/execute() | 执行线程池 |
---|---|
shutdown()/shutdownNow() | 终止线程池 |
isShutdown() | 判断线程是否终止 |
getActiveCount() | 正在运行的线程数 |
getCorePoolSize() | 获取核心线程数 |
getMaximumPoolSize() | 获取最大线程数 |
getQueue() | 获取线程池中的任务队列 |
allowCoreThreadTimeOut(boolean) | 设置空闲时是否回收核心线程 |
Executors创建线程池
FixedThreadPool(n) | 创建一个数量固定的线程池,超出的任务会在队列中等待空闲的线程,可用于控制程序的最大并发数。 |
---|---|
CachedThreadPool() | 短时间内处理大量工作的线程池,会根据任务数量产生对应的线程,并试图缓存线程以便重复使用,如果限制 60 秒没被使用,则会被移除缓存。 |
SingleThreadExecutor() | 创建一个单线程线程池。 |
ScheduledThreadPool(n) | 创建一个数量固定的线程池,支持执行定时性或周期性任务。 |
SingleThreadScheduledExecutor() | 此线程池就是单线程的 newScheduledThreadPool。 |
WorkStealingPool(n) | Java 8 新增创建线程池的方法,创建时如果不设置任何参数,则以当前机器处理器个数作为线程个数,此线程池会并行处理任务,不能保证执行顺序。 |
线程池创建Demo
public class Test002 {
private static int corePoolSize =10;
private static int maxmumPoolSize =30;
private static long keepTime = 30;
private static TimeUnit unit = TimeUnit.SECONDS;
private static ArrayBlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(1000);
private static ThreadFactory threadFactory1 = Executors.defaultThreadFactory();
private static ThreadFactory threadFactory2 = r -> {
Thread t = new Thread(r);
return t;
};
private static ThreadPoolExecutor.AbortPolicy policy = new ThreadPoolExecutor.AbortPolicy();
public static void main(String[] args) throws Exception{
ExecutorService executorService = new ThreadPoolExecutor(
corePoolSize,
maxmumPoolSize,
keepTime,
unit,
blockingQueue,
threadFactory1,
policy);
executorService.execute(new Runnable(){
public void run(){
System.out.println("new Runnable!");
};
});
executorService.execute(()->{
System.out.println("Lambada");
});
/*
//线程池拒接收新提交的任务,同时立马关闭线程池,线程池里的任务不再执行。
executorService.shutdownNow();
*/
/*
//线程池拒接收新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。
executorService.shutdown();
*/
// 这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,
// 否则返回false,一般情况下会和shutdown方法组合使用。
boolean boole = executorService.awaitTermination(3,TimeUnit.SECONDS);
}
}
ScheduledThreadPoolExecutor(定时任务线程池)
public class ScheduleExectorsService_Demo {
public static void main(String[]args)throws InterruptedException,ExecutionException{
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4);
//延迟两秒执行任务
ScheduledFuture<?> future1 =executor.schedule(()->System.out.println("延迟2秒后开始工作的线程"),2, TimeUnit.SECONDS);
//取消任务
TimeUnit.SECONDS.sleep(4);
System.out.println("都4秒了,不玩了"+Thread.currentThread().getName()+future1.cancel(true));
//延时6秒开始跑,上一次任务开始后, 每隔1秒执行一次任务。
ScheduledFuture<?> future2 =executor.scheduleAtFixedRate(()->System.out.println("开跑了1"),6,1, TimeUnit.SECONDS);
//延时6秒开始跑, 上一次任务完成后, 延迟2秒启动任务。
ScheduledFuture<?> future3 =executor.scheduleWithFixedDelay(()->System.out.println("开跑了2"),6,2, TimeUnit.SECONDS);
/*
打断线程
boolean getContinueExistingPeriodicTasksAfterShutdownPolicy():
取有关在此执行程序已 shutdown 的情况下、是否继续执行现有定期任务的策略。
boolean getExecuteExistingDelayedTasksAfterShutdownPolicy():
获取有关在此执行程序已 shutdown 的情况下是否继续执行现有延迟任务的策略
*/
// true:让正在执行的线程执行完; false:不让当前正在跑的线程顺利结束。
executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
executor.shutdown();
}
}
Future(submit执行Callable)
public class FutureExample_Demo1 {
public static void main(String [] args)throws Exception{
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
//根据8的推导特性,传入Callable。
Future<Map> future = executorService.submit(()->{
Map<String,String> map = new HashMap<>();
map.put("111","111111");
return map;
});
//直接拿。
Map result = future.get();
//延时拿(拿不到抛TimeoutException)。
result = future.get(2, TimeUnit.SECONDS);
System.out.println("从Future<Integer>中拿出结果"+result);
//判断线程是否结束。
boolean boole = future.isDone();
// 设 true 会打断,设 false 会接着跑。
boolean boole1 = future.cancel(false);
boolean boole2 = future.isCancelled();
executorService.shutdown();
}
}