一、什么是线程池
简单来说就是一个存放线程的容器,可以容纳多个线程,其中的线程可以反复使用。
二、为什么要创建线程池
线程的创建和销毁都是很耗费资源的,创建线程池之后可以减少这种消耗。同时,如果在一个jvm里创建了太多线程,也会导致系统内存被过度消耗,“切换过度”会造成系统资源不足,而线程池可以对线程的数量加以限制,也能很好的解决这个问题。
三、Executor与ExecutorService的区别
Executor | ExecutorService |
是Java线程池的核心接口,用来并发执行提交提交的任务 | ExecutorService是Executor接口的扩展,提供了异步执行和关闭线程池的方法 |
execute()方法用来提交任务,没有返回值 | 使用submit()来提交任务,返回Future对象,可以获取任务执行结果 |
不能取消任务 | 可以用Future.cancel()取消pending中的任务 |
没有关闭线程池的方法 | 提供了关闭线程池的方法 |
四、Executors
提供工厂方法来创建不同类型的线程池,下面是它所提供的四种线程池:
(1)newCachedThreadPool:线程池根据需求创建线程,可扩容。
特点:1.线程池数量不固定,可达到最大值
2.线程池中的线程可以进行缓存重复利用和回收(回收的时间默认一分钟)
3.当线程池中没有可用线程,会重新创建一个线程
适用于创建一个可无限扩大的线程池,服务器负载压力比较轻,执行时间较短,任务多的场景
(2)newSingleThreadExecutor:一个任务一个任务执行,一池一线程
特点:线程中最多执行1个线程,之后提交的线程活动将会排在队列中等待执行,提交顺序就是执行顺序
适用于需要保证执行顺序,并且在任意时间不会有使用多个线程的场景
(3)newFixedThreadPool(int):一池N线程
特点:1.线程池中的线程处于一定的量,可以很好的控制线程的并发量
2.线程可以重复使用,在显示关闭之前,都将一直存在
3.超出量的线程被提交的时候需在队列中等待
使用于可以预测线程数量的业务中,或者服务器负载比较重,对线程有严格限制的场景
(4)newScheduledThreadPool (了解)
创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
场景: 适用于需要多个后台线程执行周期任务的场景
都是通过new ThreadPoolExecutor来构造线程池,线程池相关参数:
- corePoolSize:核心线程数量(常驻线程),一般情况下不管有没有任务都会在线程池中一直存活
- maximumPoolSize:线程池所能容纳的最大线程数,当活动的线程数达到这个值后,后续的新任务将被阻塞
- keepAliveTime:线程闲置超时的时长,当线程的闲置时长超过这个数后,会被终止,一般用于非核心线程
- unit:时间单位,用于指定keepAliveTime参数的时间单位
- workQueue:任务队列(阻塞队列),当线程数超过核心线程数时,新任务会放到阻塞队列中等待执行
- threadFactory:线程工厂,是一个接口,用来为线程池创建新线程
- RejectedExecutionHandler:拒绝策略
拒绝策略:
- AbortPolicy(默认):直接抛出RejectedExecutionException异常组织系统正常运行
- CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务退回到调用者,降低新任务的流量
- DiscardOldestPolicy:摒弃队列中等待最久的任务,把新的任务加入到队列中,尝试再次提交当前任务
- DiscardPolicy:默默丢弃无法处理的任务,不做处理也不抛出异常。如果任务允许丢失,这将是最好的一种策略。
五、线程池的关闭
ThreadPoolExecutor 提供了两个方法,用于线程池的关闭,分别是 shutdown() 和shutdownNow()。
shutdown():不会立即的终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。
六、代码实现
public class ThreadPoolDemo {
public static void main(String[] args) {
// 一池5线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 一池一线程
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 一池可扩容线程
// ExecutorService threadPool = Executors.newCachedThreadPool();
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
for (int i = 1; i <=10; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "为第"+index+"个任务服务");
}
});
}
threadPool.shutdown();
}
}