一、线程池创建
承接深入剖析Java创建线程的方式(学习向)-优快云博客,创建线程池有两种方式:
1. 调用Executors类的静态方法
java.util.concurrent包下的Executors类,有很多静态方法,例如newFixedThreadPool(), newWorkStealingPool(), newSingleThreadExecutor()等来创建线程池,但看源码,其实还是调用ThreadPoolExecutor,只是用写好的参数构造罢了。这种方法由于传参不当,可能导致OOM,因此很少用
2. 手动创建ThreadPoolExecutor实例
同样java.util.concurrent包下的ThreadPoolExecutor,才是我们熟知的线程池类,我们可以自定义参数来解决OOM的问题
二、线程池参数+工作原理
参数:
corePoolSize | 核心线程数 | 默认不会销毁,从而达到线程复用 |
maximumPoolSize | 最大线程数 | 核心线程都在占用时,对新任务启动临时线程。临时线程最大数量+核心线程数=最大线程数 |
keepAliveTime | 临时线程的最大存活时间 | 临时线程在无任务的keepAliveTime时间后会自动销毁 |
unit | 存活时间的单位 | keepAliveTime的单位 |
threadFactory | 创建线程使用的工厂 | 创建线程使用的工厂 |
workQueue | 任务等待队列 | 最大线程数后仍然有新任务,则任务加入等待队列 |
handler | 拒绝策略 | 等待队列满了会拒绝新任务(有不同拒绝策略) |
工作原理:
1. 创建线程池
2. 新任务加入,创建核心线程(也可以用prestartCoreThread()提前创建)
3. 新任务加入,若有空闲核心线程则复用,无则创建
4. 重复3……直到核心线程都创建完,且都在被占用
5. 新任务加入,创建临时线程来处理任务(处理完且空闲时间超过keepAliveTime则销毁)
6. 重复5……直到临时线程也创建满了(核心+临时线程数达到最大线程数)
7. 新任务加入,进入等待队列
8. 重复7……等待队列也满了
9. 还有新任务加入,采用拒绝策略
三、拒绝策略有哪些
1. 抛异常 (RejectedExecutionException)
2. 丢弃队列最前边的任务(DiscardOldestPolicy)
3. 丢弃这个新任务(DiscardPolicy)
4. 调用线程池的线程自己运行该任务(CallerRunsPolicy)
四、如何使用线程池
先创建线程池:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
2, // 最大线程数
0L, TimeUnit.MILLISECONDS, // 线程空闲时间
new LinkedBlockingQueue<Runnable>() // 任务队列
);
1. 调用execute()
// 使用execute方法提交任务,execute方法不返回结果,适用于不需要返回值的任务
executor.execute(() -> {
System.out.println("使用execute方法创建的线程正在运行,线程名: " + Thread.currentThread().getName());
});
execute只能提交Runnable任务,因此无返回值
2. 调用submit()方法
// 提交任务给线程池执行,返回一个Future对象,可以获取任务的执行结果
Future<?> future = executor.submit(() -> {
System.out.println("使用线程池创建的第一个线程正在运行,线程名: " + Thread.currentThread().getName());
return "任务执行结果";
});
try {
// 尝试获取submit方法提交任务的结果
Object result = future.get();
System.out.println("submit方法提交的任务执行结果: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
submit可以提交Runnable任务或Callable任务,Callable任务可以获得返回值
五、补充的小知识
5.1 核心线程也可以自动回收
核心线程是默认不回收的,但也可以设置回收:
allowCoreThreadTimeOut属性设为 true 就可以跟临时线程一样空闲定时回收了
但是注意:核心线程的本意就是减小线程创建和销毁的资源消耗,如果频繁回收、创建就失去了它的意义
5.2 空闲的核心线程是什么状态
核心线程一般是不会回收的,而要执行任务的时候会拿来直接运行,因为有同学会任务它在空闲时是就绪态。
其实是waiting,等待状态
如果设置了allowCoreThreadTimeOut=true,就是timed_waiting 超时等待。
我们要明白就绪态的意义:逻辑上正在运行,只是CPU没在处理,可能几毫秒后就运行了
而运行态:逻辑上在运行,CPU也恰好在处理