参考
介绍
Executors 工具类创建线程实现是调用的 ThreadPoolExecutor,但是隐藏了部分细节和参数设置。并且阿里巴巴代码规范也禁止使用 Executors 工具类创建线程池。
创建方法 | 描述 |
---|---|
Executors 工具类三种方法创建线程 | 堵塞等待执行(这里不确定,可能有其他方法可以指定策略来堵塞或抛弃任务) |
ThreadPoolExecutor | 根据配置的拒绝策略来执行 |
Executors 方法
方法名 | 描述 |
---|---|
Executors.newSingleThreadExecutor() | 单一线程 |
Executors.newFixedThreadPool(int nThreads) | 指定线程数量 |
Executors.newCachedThreadPool() | 无限制,自动扩容与缩容(最大21亿) |
ThreadPoolExecutor 介绍
-
参数列表
参数名 描述 int corePoolSize 核心线程数(不会被关闭) int maximumPoolSize 最大线程数(当队列满了之后,就会适当开启线程) long keepAliveTime 当前线程数大于核心线程数,并且某些线程空闲一定时间后,会被关闭 TimeUnit unit 超时时间单位 BlockingQueue workQueue 指定使用的阻塞队列类型与设置队列的大小 ThreadFactory threadFactory 线程工厂,主要用来创建线程,默认为正常优先级、非守护线程 RejectedExecutionHandler handler 拒绝策略,有四种 -
拒绝策略
策略名 描述 ThreadPoolExecutor.AbortPolicy 满了(最大线程数+队列大小)就报错,但是已经进入的任务会执行完毕,这里 shutdown() 方法要放在 finally 中 ThreadPoolExecutor.CallerRunsPolicy 满了(最大线程数+队列大小)拒绝/丢弃,直接在execute方法的调用线程中运行被拒绝的任务(main线程) ThreadPoolExecutor.DiscardOldestPolicy 满了(最大线程数+队列大小)就拒绝/丢弃最早进入并且未处理的任务 ThreadPoolExecutor.DiscardPolicy 满了就拒绝/丢弃
Executors 不推荐
package pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @Author 夏秋初
* @Date 2022/3/3 14:22
*/
public class Test {
public static void main(String[] args) {
// 单一线程
// ExecutorService executorService = Executors.newSingleThreadExecutor();
// 固定线程数量
// ExecutorService executorService = Executors.newFixedThreadPool(2);
// 最大21亿
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
// 必须手动关闭
executorService.shutdown();
}
}
ThreadPoolExecutor 推荐
package pool;
import java.util.concurrent.*;
/**
* @Author 夏秋初
* @Date 2022/3/3 14:22
*/
public class Test2 {
public static void main(String[] args) {
// 线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
// 核心线程数(不会被关闭)
2,
// 最大线程数(当队列满了之后,就会适当开启线程)
5,
// 当前线程数大于核心线程数,并且某些线程空闲一定时间后,会被关闭
10,
// 超时时间单位,当前例子是 10s
TimeUnit.SECONDS,
// 指定使用的阻塞队列类型与设置队列的大小
new LinkedBlockingDeque<Runnable>(3),
// 线程工厂,主要用来创建线程,默认为正常优先级、非守护线程
Executors.defaultThreadFactory(),
/**
* 拒绝策略
* ThreadPoolExecutor.AbortPolicy 满了就报错,但是已经进入的任务会执行完毕,这里 shutdown() 方法要放在 finally 中
* ThreadPoolExecutor.CallerRunsPolicy 满了丢弃,直接在execute方法的调用线程中运行被拒绝的任务
* ThreadPoolExecutor.DiscardOldestPolicy 满了就丢弃最早进入并且未处理的任务
* ThreadPoolExecutor.DiscardPolicy 满了就丢弃
*/
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try{
for (int i = 0; i < 20; i++) {
final int temp = i;
threadPoolExecutor.execute(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+temp);
});
}
}finally {
// 必须手动关闭
threadPoolExecutor.shutdown();
}
}
}
IO 密集型与 CPU 密集型
代码中不建议把线程数写成固定的,可以通过 Runtime.getRuntime().availableProcessors() 来获取 CPU 线程数量
类型 | 数量值 | 原因 |
---|---|---|
CPU 密集型 | 最大线程数=CPU线程数+1 | 对于计算要求高,让线程尽快执行 |
IO 密集型 | 最大线程数=CPU线程数*2 | 占用资源,容易堵塞,耗时比较久,可以给别的线程运行机会 |