一、为什么要有线程池?
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序
都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
1.降低资源的消耗。通过重复利用已创建的线程降低线程的创建和销毁造成的消耗。
2.提高响应效率。当任务到达时,任务可以不需要等待线程创建就能执行。
3.提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
二、线程池类的继承关系?
1.接口以及方法
Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable)用来执行传进去的任务,然后ExecutorService接口继承了Executor接口,声明了方法:submit、invokeAll、invokeAny以及shutDown等,抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;然后ThreadPoolExecutor继承了类AbstractExecutorService。
三、ThreadPoolExecutor的研究
1、属性
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//容量的大小2^29-1;
线程池中的线程有五种状态
private static final int RUNNING = -1 << COUNT_BITS;//接收新任务并且处理队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;//不接收新的任务,但是处理队列中的任务
private static final int STOP = 1 << COUNT_BITS;//不接收新的任务,不处理队列中的任务,中断正在处理任务
private static final int TIDYING = 2 << COUNT_BITS;//所有的任务都停止了,工作线程的数目为0,线程在转换到该状态时会调用中断方法
private static final int TERMINATED = 3 << COUNT_BITS;//调用了中断方法
2.构造函数
|
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务执行时最多保持多久时间会终止
unit:参数keepAliveTime的时间单位
workQueue:阻塞队列
threadFactory:线程工厂
handler:拒绝处理任务时的策略
策略:
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
DiscardPolicy:丢弃任务,但是不抛出异常。
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
CallerRunsPolicy:由调用线程处理该任务
四、Executors静态工厂几种常用线程池
newCachedThreadPool | 可缓存的线程池。必要的时候创建新线程来处理请求,也会重用线程池中已经处于可用状态的线程。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程;当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制 | 适合于耗时短,不需要考虑同步的场合 |
newScheduledThreadPool | 可定时运行(初始延时),运行频率(每隔多长时间运行,还是运行成功一次之后再隔多长时间再运行)的线程池 | 适合定时以及周期性执行任务的场合 |
newFixedThreadPool | 固定大小的线程池,线程池满且线程都为活动状态的时候如果有新任务提交进来,它们会等待直到有线程可用。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程 | 符合常用场合 |
newSingleThreadExecutor | 一个单线程的线程池,只有一个线程在工作(如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。)能够保证所有任务的执行顺序按照任务的提交顺序执行,同一时段只有一个任务在运行 | 适合于需要保证执行顺序的场合 |