Java线程池总结
一、线程池的概念及优势
线程在使用时需要创建,使用完需要销毁,而创建、销毁线程会消耗系统资源(内存、CPU),重复操作会导致大量资源浪费。从而就需要使用池化技术----线程池,减少线程对象的创建、回收次数,提高服务效率。
线程池具有以下几点优势:
1.降低资源销毁:
通过重复利用已经创建的线程,降低线程创建和销毁造成的消耗
2.提高响应速度
当任务到达时,任务可以不需要等到线程创建就能立即执行
3.防止服务器过载
不会形成内存溢出,或者CPU耗尽
4.提高线程的可管理性
线程是稀缺资源,如果无限制地创建,不仅会消耗资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控
二、线程池的参数及选择策略
线程池的真正实现类是 ThreadPoolExecutor,来自于抽象类 AbtractExecutorService,而 AbtractExecutorService实现了ExecutorService接口,ExecutorService接口继承于Executor接口
1.构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(),handler);
}
2.其中的几个参数:
corePoolSize:线程池中的核心线程数量,在没有用的时候,也不会被回收
maximumPoolSize:线程池中可以容纳的最大线程的数量
keepAliveTime:线程池中除了核心线程之外的其他的最长可以保留的时间,因为在线程池中,除了核心线程即使在无任务的情况下也不能被清除,其余的都是有存活时间的,意思就是非核心线程可以保留的最长的空闲时间
util:计算时间的一个单位
workQueue:等待队列,任务可以储存在任务队列中等待被执行,执行的是FIFIO原则(先进先出)
handler:拒绝策略
其中有4种拒绝策略:
a.AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满,默认该方式。
b.CallerRunsPolicy:直接调用execute来执行当前任务。
c.DiscardPolicy:丢弃任务,但是不抛出异常。
d.DiscardOldestPolicy:抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去。先从任务队列中弹出最先加入的任务,空出一个位置,然后再次执行execute方法把任务加入队列。
3.参数选择策略:
线程池核心线程数的设置?
cpu密集型:cpu个数+1
io密集型:2*cpu个数+1
1.高并发、任务执行时间短的业务怎样使用线程池?
高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换
2.并发不高、任务执行时间长的业务怎样使用线程池?
假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务。
假如是业务时间长集中在计算操作上,也就是计算密集型任务,线程池中的线程数设置得少一些,减少线程上下文的切换
三、常见的4种线程池
newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- 特点:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。
- 应用场景:执行大量、耗时少的任务。
创建方式:
ExecutorService pool=Executors.newCachedThreadPool();
pool.execute(()->{
});
newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- 特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
- 应用场景:控制线程最大并发数。
创建方式:
ExecutorService pool=Executors.newFixedThreadPool(2);//固定两个线程
pool.execute(()->{
});
newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
- 特点:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列。
- 应用场景:执行定时或周期性的任务。
创建方式:
ExecutorService pool=Executors.newScheduledThreadPool(5);//5个线程
pool.scheduleAtFixedRate(()->{
},0,2,TimeUnit.SECONDS);//每两秒执行一个线程
newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
创建方式:
ExecutorService pool=Executors.newSingleThreadExecutor();
pool.execute(()->{
});
- 特点:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。
- 应用场景:不适合并发但可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等。