一.为什么需要线程池?
用于管理和复用线程,提高应用程序的性能、响应性和资源利用率。
1.降低线程创建和销毁的开销: 线程的创建和销毁是一项开销较大的操作。通过线程池,可以避免频繁地创建和销毁线程,以提高系统的性能。
2.控制并发线程数量:线程池可以限制系统中并发执行的线程数量,避免因线程过多导致系统资源被耗尽的问题,提高系统的稳定性和可靠性。
3.提高任务执行速度:线程池可以实现任务的并发执行,提高任务的执行速度,特别是在多核处理器上能够更好地利用硬件资源。
4.统一管理线程:线程池可以集中管理线程的生命周期、状态和行为,简化线程管理的复杂性,提高代码的可维护性和可读性。
5.提供任务队列:线程池通常包含一个任务队列,用于存储等待执行的任务。通过任务队列,可以实现任务调度和控制,避免任务因无法立即执行而被丢弃。
6.灵活的线程调度策略:线程池提供了多种线程调度策略,如固定大小的线程池、可变大小的线程池、定时任务线程池等,可以根据实际需求选择合适的策略。
7.避免资源竞争和线程安全问题:通过线程池,可以避免多个线程同时竞争同一个资源带来的线程安全问题,提高程序的稳定性和可靠性。
二.java中常用线程池
public class ThreadPool {
/**
*固定大小的线程池:创建一个固定大小的线程池,该线程池中始终保持固定数量的线程在工作状态,如果有任务提交,会放入任务队列中等待执行。
* @return
*/
public ExecutorService fixThreadPool(){
return Executors.newFixedThreadPool(5);
}
/**
* 缓存线程池:创建一个可以根据需要创建新线程的线程池,线程池中的线程数量不固定,可自动增加或减少。
* @return
*/
public ExecutorService cachePool(){
return Executors.newCachedThreadPool();
}
/**
* 单例线程池:用于顺序执行任务
* @return
*/
public ExecutorService singleThreadPool(){
return Executors.newSingleThreadExecutor();
}
/**
* 定时任务线程池:创建一个可以执行定时任务的线程池,可以用来执行定时任务或周期性任务
* @return
*/
public ExecutorService scheduledThreadPool() {
return Executors.newScheduledThreadPool(3);
}
/**
*单线程定时任务线程池
* 使用场景:定时执行任务、延迟执行任务、周期性执行任务、需要保证任务按照顺序执行
* executor.schedule()
* executor.scheduleAtFixedRate()
* @return
*/
public ExecutorService singleScheduledThreadPool(){
return Executors.newSingleThreadScheduledExecutor();
}
/**
* 工作窃取线程池(Work-Stealing ThreadPool):
* 用途:工作窃取线程池适用于执行可以被分解为较小任务的任务集合,通常在执行递归或分治算法时非常有效。
* 特性:
* 每个工作线程都有一个任务队列来存储待执行的任务。
* 当一个线程完成自己的任务后,会从其他线程的任务队列中“窃取”任务执行,以保持线程的高效利用。
* 这种机制可以减少线程间的竞争,提高并行执行效率。
* @return
*/
public ExecutorService newWorkStealingPool() {
return Executors.newWorkStealingPool();
}
/**
* 7大参数创建线程池
* corePoolSize:核心线程数,在线程池中始终保持活动的线程数量,即使它们处于空闲状态也不会被销毁,当有任务到达时,核心线程会立即执行任务,而不需要等待。
* maxPoolSize:最大线程数
* keepAliveTime:超时时间
* unit:超时单位
* workQueue:阻塞队列
* threadFactory:线程工厂
* handler:拒绝策略
*
* 线程池的运行原理:新任务提交-->核心线程数是否用完?
* 核心线程没用完:直接执行任务
* 核心线程用完:则看任务队列是否已满?
* 任务队列没满:加入队列等待执行
* 任务队列已满:线程池是否已满即线程池中的线程数是否等于最大线程数 ? 执行拒绝策略 : 创建线程执行任务
* 4钟拒绝策略:
* AbortPolicy:默认拒绝策略,当线程池无法处理新任务且队列已满时,会抛出RejectedExecutionException异常
* CallerRunsPolicy:让提交任务的线程自己执行该任务。也就是说,如果线程池无法接受新任务,则由提交任务的线程自己来执行该任务。
* DiscardPolicy:会默默地丢弃无法处理的任务,不会给出任何提示。如果线程池无法接受新任务且队列已满,新任务将被丢弃。
* DiscardOldestPolicy:会丢弃队列中等待时间最长的任务,然后将新任务加入队列中。这样可以尝试为新任务腾出空间。
* @return
*/
public ExecutorService threadPoolExecutor() {
ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100); // 任务队列
return new ThreadPoolExecutor(3,
10,
30,
TimeUnit.SECONDS,
workQueue,
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}