引入
- 线程不仅是Java中的一个对象,每个线程都有自己的工作内存
线程的创建、销毁需要时间,过多的操作会消耗性能
线程过多也会占用内存
- 操作系统需要频繁切换上下文,会影响性能
- 如果线程 创建时间 + 销毁时间 > 执行任务时间 则不合算。
线程池组成
-
线程池管理器
用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务 -
工作线程
线程池中线程,可以循环的执行任务,在没有任务是处理等待状态 -
任务接口
每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行后的收尾工作,任务执行的状态等 -
任务队列
用于存放没有处理的任务,提供一种缓存机制
典型的ThreadPoolExecutor的创建
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, //核心线程数
5, //最大线程数
5, //keepAliveTime ,超过核心线程数的线程,超过keepAliveTime,这个线程湖北销毁
TimeUnit.SECONDS, //keepAliveTime 的时间单位
new LinkedBlockingDeque<Runnable>(5) //传入边界为5的工作队列
);
线程池执行时,各创建参数作用流程图
无解队列与拒绝策略
线程池类的层次结构
队列和拒绝策略API
workQueue队列
- SynchronousQueue (同步移交队列)
队列不作为任务的缓冲方式,它不会为队列中元素维护存储空间。可以简单理解为队列长度为零 - LinkedBlockingQueue (无界队列)
队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务处理速度,造成请求堆积)可能导致内存占用过多或OOM - ArrayBlockintQueue (有界队列)
队列长度受限,队列满了就会创建多余的线程来执行任务
handler拒绝策略
- AbortPolicy
中断抛出异常 - DiscardPolicy
默默丢弃任务,不进行任何通知 - DiscardOldestPolicy
丢弃掉队列中存在时间最久的任务 - CallerRunsPolicy
让提交任务的线程去执行任务(对比前三种谁比较友好)
常见的线程池创建API
- Executors 工具类
不知道任务需要多少线程,每需要一个线程就会创建一个线程
- newFixedThredPool(int nThreads)
创建一个固定大小、任务队列容量无解的线程池
核心线程数 = 最大线程数
弊端:当请求过多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致占用过多内存或直接导致OOM异常
ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
等同于
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5,
5,
0,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
- newCachedThreadPool()
创建的是一个大小无界的缓冲线程池。它的任务队列是一个同步队列。任务加入到池中,如果池中有空闲线程,则用空闲线程,如无则创建新线程执行,池中的线程空闲超过60秒,将被销毁释放。线程数随任务的多少变化
适用于执行时消耗较小的异步任务。池的核心线程数=0,最大线程数=Integer.Max_VALUE
弊端:弊端和newFixedThreadPool一致
ThreadPoolExecutor cachedPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
等同于
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
0, //核心线程数0
Integer.MAX_VALUE, //最大线程数是无限大
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()
);
- newSigleThreadExecutor()
只有一个线程来执行无界任务队列的单一线程池。该线程池确保任务按加入的顺序一个一个依次执行。当唯一的线程因任务异常中止时,将创建一个新的线程来执行后续的任务。与newFixedThredPool(1)的区别在于,单一线程池的大小在newSigleThreadExecutor方法中硬编码,不能再改变
弊端: 每次来请求直接创建新线程来处理任务,也不使用队列缓冲,会自动回收多余线程,由于将maxPoolSize设置成Integer.MAX_VALUE,当请求很多时就可能创建过多的线程,导致资源耗尽OOM
- newScheduledThreadPool(int corePoolSize)
能定时执行任务的线程池。该线程池的核心线程数由参数指定,最大线程数=Integer.MAX_VALUE
弊端:弊端同newCachedThreadPool一致
线程池关闭
- shutdown()
线程池中无任务时会结束
后续进来的任务会被执行拒绝策略
- shutdownNow()
线程池中立刻关闭
会返回未进行完毕线程
线程数量
如何确定适合数量的线程
计算型任务
cpu数量的1-2倍
I/O型任务
相对比计算型任务,需多一些线程,要根据具体的IO阻塞时长进行考量决定。也可以考虑根据需要在一个最小数量和最大数量间自动增减线程数
如tomcat中的最大线程数为200