ExecutorService详细介绍(ThreadPoolExecutor)
ExecutorService的主要实现类为ThreadPoolExecutor,网上搜索ExecutorService的使用,很多情况下搜索出来的是通过Executors来进行创建的,但是使用Executors会带来一些问题,阿里java规范文档也明确禁止使用Executors创建线程池(如果想要了解为什么不使用Executors创建线程,请看这篇文章阿里为何禁用Executors创建线程池)。其实Executors底层也是使用ThreadPoolExecutor进行创建的,只是使用Executors可以帮我们少些一些参数,我这里就使用ThreadPoolExecutor直接创建对象,ThreadPoolExecutor的参数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
corePoolSize 线程池核心数(程序运行的正常线程数量,从0开始增加)
maximumPoolSize 最大线程数(工作队列满了以后,会继续创建线程,直到线程数量大于maximumPoolSize后抛执行错误策略,这里要注意maximumPoolSize要大于corePoolSize,否则抛异常)
keepAliveTime 闲置时间(当线程数大于corePoolSize,且有闲置的线程,则当线程经过了keepAliveTime时间后会被销毁)
unit 用于描述keepAliveTime的
BlockingQueue<Runnable> workQueue 工作队列(当线程数大于corePoolSize时,会进入工作队列进行等待)
ThreadFactory 线程工厂(用于创建线程的工程,一般使用默认的)
RejectedExecutionHandler 错误策略(当线程数超过maximumPoolSize后,该错误策略将被执行)
这里粘贴一下我报的网课老师画的图,方便大家理解这几个参数
来一个小Demo来认识一下这些参数的作用,这里的BlockingQueue、RejectedExecutionHandler和ThreadFactory都是使用最简单的,如果想要更深入了解,可以自行百度,后续有时间我再补充。
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(2, //核心线程数为2
5, //最大线程数为5
10, TimeUnit.SECONDS, //设置10秒过期
new LinkedBlockingDeque(2), //队列大小为2
new MyThreadFactory(),//这里用简单的线程工厂
new RejectedExecutionHandler() {
@Override
//错误策略,超过最大线程数后执行该方法
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("ThreadName:" + Thread.currentThread().getName() + "执行拒绝策略:");
}
});
for (int i = 0; i < 10; i++) {
Runnable runnable = new MyRunnable();
executorService.execute(runnable);
try {
System.out.println("创建线程:" + i);
Thread.sleep(1000);//1秒创建一个
} catch (InterruptedException e) {
e.printStackTrace();
}
}
executorService.shutdown();
}
}
class MyThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("ThreadName:" + Thread.currentThread().getName() + "线程执行:");
try {
Thread.sleep(8000);//每个线程休息8秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果为
创建线程:0
ThreadName:Thread-0线程执行:
创建线程:1
ThreadName:Thread-1线程执行:
创建线程:2
创建线程:3
创建线程:4
ThreadName:Thread-2线程执行:
创建线程:5
ThreadName:Thread-3线程执行:
创建线程:6
ThreadName:Thread-4线程执行:
创建线程:7
ThreadName:main执行拒绝策略:
ThreadName:Thread-0线程执行:
创建线程:8
ThreadName:Thread-1线程执行:
创建线程:9
ThreadName:Thread-2线程执行:
ThreadName:Thread-3线程执行:
我们来分析一下结果:
1、核心线程数为2,所以前面两条0、1线程被创建后立马执行了;
2、第2和第3线程创建好后并没有立即执行,而是进入了等待队列;
3、第4、5、6创建后立马执行了,说明队列满了,所以重新创建线程;
4、第7线程执行时候,由于线程已经达到核心线程数了,所以执行了错误策略;
5、第8、9线程,由于runnable里面只停留8秒,所以此时0线程被释放了,但是等待队列也释放了,如果8、9线程抢到执行权,则直接执行,否则进入到等待队列中;
6、最后执行等待队列中的线程,到此整个流程结束;