这里先简述一下,要执行一个线程(任务)时,不是new一个线程,而是从线程池获取一个存在的线程,执行完毕后,把该线程归还到线程池,等待下次执行任务。
线程池是一个对象池。对象池是一种常见的系统优化技术。其核心思想是,一个类被频繁使用时,没必要每次都初始化一个对象,可以将它的一些实例保存在一个容器中,使用时向容器请求,用完后归还容器,达到复用对象的目的,减少对象创建和销毁时的系统消耗。这里的容器就是对象池。线程池本质上是一个线程对象的对象池,由于对象的创建和销毁会有一定的耗时,使用线程池复用线程有利于提高系统性能。
对象池的使用场景:一些大型对象被频繁调用,大型对象指创建或销毁比较耗时的对象
常见的对象池有:线程池,数据库连接池。
1)降低资源消耗
从对象池的知识我们知道,线程池降低了线程创建和销毁的资源消耗
2)提高响应速度
线程池减少了创建和销毁线程池的时间,提高了响应速度
3)便于管理线程
线程池提供了关闭线程池、指定核心线程数等操作,方便了管理线程。
JAVA给我们定义好了几种常见的线程池:
1)生成固定线程数的线程池
Executors.newFixedThreadPool(int nThreads)
2)生成一个可缓存的线程池
Executors.newCachedThreadPool()
3)生成单线程的线程池
Executors.newSingleThreadExecutor()
核心类是ThreadPoolExecutor类,继承AbstractExecutorService抽象类(写了线程池的一些默认实现),AbstractExecutorService实现了ExecutorService接口(该接口定义了线程池的操作关闭、立即关闭、提交等操作),ExecutorService接口继承自Executor接口(定义execute方法)。
Executors类,在这里是工厂类。
一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉
如果设置allowCoreThreadTimeOut(boolean value),则也会作用于核心线程
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于 ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处 于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
AbortPolicy:直接抛出异常。
CallerRunsPolicy:只用调用者所在线程来运行任务。
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉。
当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
corePoolSize->workQueue->maxPoolSize
1) 当未超过核心线程数时,直接创建一个核心线程去执行任务
2) 当超过核心线程数时,将任务加入workQueue中等待执行
3) 当workQueue满时,在不超过maxPoolSize的情况下,启动线程去处理任务
4、当线程数量超过maxPoolSize时,调用拒绝策略
以下是流程图:
8、理解默认线程池实现
1) FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
FixedThreadPool,创建固定核心线程数的线程池。提交任务时,nThread 2)CachedThreadPool CachedThreadPool,corePoolSize=0,maxPoolSize=MAX_Value。提交任务时,nThread永远>corePoolSize,同时SynchronousQueue是一个不存储元素的阻塞队列,所以CachedThreadPool提交任务时会尝试获取线程,若获取到则执行,若获取不到,则创建线程执行任务。任务执行完毕后,线程归还线程池,在无调用的情况下,存活时间是60s。 3)SingleThreadPool SingleThreadPool,corePoolSize=1,maxPoolSize=1。该线程池只有一个核心线程,最多允许一个线程。若任务正在执行,新提交的任务将会进入阻塞队列等待执行。 1)CUP密集型任务,就需要压榨CUP,N=Ncpu+1 2)IO密集型任务,参考值可为 N=2xNcpu 以上仅是参考值,最佳大小需要自己调试。 参考文献: 文献1:https://blog.csdn.net/u010983881/article/details/79322499 文献2:https://www.jianshu.com/p/e66e9924a953 文献3:https://www.cnblogs.com/dolphin0520/p/3932921.html 文献4:《JAVA程序性能优化》-葛一鸣 public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue