ThreadPoolExecutor的参数含义
如果你在工作中用到线程池了,那一定要用 ThreadPoolExecutor 这个类,这个类里面有常用5个参数(实际上是7个),但是呢,有几种情况,使用的时候往往分不清,之后觉得线程池很难,今天我就用例子来说明一下具体用法,今后不再犯难ThreadPoolExecutor。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
...
}
corePoolSize:核心线程数,包括空闲线程
maximumPoolSize:线程池里允许存在的最大的线程数
keepAliveTime:设置非核心线程的空闲线程的存活时间。当线程数量>核心线程数,并且不大于最大线程数,并且队列用的是 SynchronousQueue对列的时候,这个参数就起作用了:在线程执行完任务后的指定时间内不将这个空闲线程删除,超过时间后,则将这个空闲线程删除。注意删除的空闲线程是非核心线程。
unit:是 keepAliveTime的时间单位,比如秒,分钟之类的
BlockingQueue:存放未执行的任务队列
ThreadPoolExecutor是如何工作的
定义公共的任务
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "在运行" + System.currentTimeMillis());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
当前线程数<=corePoolSize,马上使用核心线程执行任务,无论用 LinkedBlockingDeque还是
SynchronousQueue 队列都不会放入工作队列里(因为没有那么多任务,这些任务都不够核心线程处理的),由于都是核心线程在处理,所以
keepAliveTime 设置0L或者其他时间都不会生效的。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 8, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
for (int i = 0; i < 5; i++) {
threadPoolExecutor.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor.getPoolSize());
System.out.println("A===" + threadPoolExecutor.getQueue().size());
Thread.sleep(3000);
System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor.getPoolSize());
System.out.println("B===" + threadPoolExecutor.getQueue().size());
结果
核心线程数无论是开始还是到最终都是5(核心线程数不会被删除,只是执行完任务后成为空闲状态),当前线程数也是5,队列数为0
对于 ThreadPoolExecutor 的下面方法可以这样理解:
threadPoolExecutor.getCorePoolSize():车的标准载客人数
threadPoolExecutor.getPoolSize():车中正在载客数
threadPoolExecutor.getMaximumPoolSize():车最大的载客人数(可以超载)
threadPoolExecutor.getQueue().size():车后面的拓展车厢载客人数
maximumPoolSize>=当前线程数>corePoolSize,并且使用的是LinkedBlockingDeque队列的话,那么马上使用核心线程执行任务,多余的任务放到这个队列里等待执行,每当核心线程里有一个线程执行完,那么就去队列拿一个继续执行,所以永远不会存在非核心线程,自然
keepAliveTime 设置0L或者其他时间都不会生效的。
ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(5, 8, 10L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
for (int i = 0; i < 7; i++) {
threadPoolExecutor1.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor1.getPoolSize());
System.out.println("A===" + threadPoolExecutor1.getQueue().size());
Thread.sleep(3000);
System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor1.getPoolSize());
System.out.println("B===" + threadPoolExecutor1.getQueue().size());
结果
执行的时候,核心线程还是5个,正在工作的也是5个,还有2个放在队列里等待执行,一旦核心线程执行完任务就去队列里取任务继续执行,直到都完成
注意:此时为什么不启动非核心线程,而是要放在队列里呢?就是因为使用的是LinkedBlockingDeque队列,理论上这个队列默认情况下是Integer.MAX_VALUE这个长度的,队列不满,不足以去启动非核心线程。而SynchronousQueue队列不一样,它没有容量之说,随存随取,否则就堵塞,所以这个队列必须启动非核心线程。
maximumPoolSize>=当前线程数>corePoolSize,并且使用的是SynchronousQueue队列的话,那么马上使用核心线程执行任务,同时再启动非核心线程去执行多余的任务,此时这个
keepAliveTime就生效了,如果设置为0L则表示这个非核心线程执行完后就删除了,如果设置非0L,则表示在非核心线程执行完后制定时间后删除。
当设置0L,执行完就删除了
ThreadPoolExecutor threadPoolExecutor1=new ThreadPoolExecutor(5,8,0L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
for (int i=0;i<7;i++){
threadPoolExecutor1.execute(runnable);
}
Thread.sleep(500);
System.out.println("A==="+threadPoolExecutor1.getCorePoolSize());
System.out.println("A==="+threadPoolExecutor1.getPoolSize());
System.out.println("A==="+threadPoolExecutor1.getQueue().size());
System.out.println("休息2秒==="+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("B==="+threadPoolExecutor1.getCorePoolSize());
System.out.println("B==="+threadPoolExecutor1.getPoolSize());
System.out.println("B==="+threadPoolExecutor1.getQueue().size());
System.out.println("结束了==="+System.currentTimeMillis());
结果
当设置3L,到期后才会删除
ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(5, 8, 3L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
for (int i = 0; i < 7; i++) {
threadPoolExecutor1.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor1.getPoolSize());
System.out.println("A===" + threadPoolExecutor1.getQueue().size());
System.out.println("休息2秒===" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor1.getPoolSize());
System.out.println("B===" + threadPoolExecutor1.getQueue().size());
System.out.println("休息5秒===" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor1.getPoolSize());
System.out.println("B===" + threadPoolExecutor1.getQueue().size());
System.out.println("结束了===" + System.currentTimeMillis());
结果
当前线程数>maximumPoolSize,并且使用的是LinkedBlockingDeque队列的话,那么马上使用核心线程执行任务,多余的任务放到这个队列里等待执行,每当核心线程里有一个线程执行完,那么就去队列拿一个继续执行,所以永远不会存在非核心线程,即使当前线程数已经超过了最大的线程数,但是由于队列基本无限大,基本不可能满,此时最大线程数都没有什么意义了,真正工作的只有核心线程,其余的都去排队了,自然 keepAliveTime 设置0L或者其他时间都不会生效的。所以newFixdThreadExecutor和newSingleThreadExecutor才会创立核心线程数和最大线程一致吧,并且超时时间设置成0(其实和第二种情况是一样的,之所以再重复一遍是为了与下面这种情况做对比)
ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(1, 2, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
for (int i = 0; i < 5; i++) {
threadPoolExecutor1.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor1.getPoolSize());
System.out.println("A===" + threadPoolExecutor1.getQueue().size());
Thread.sleep(2000);
System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor1.getPoolSize());
System.out.println("B===" + threadPoolExecutor1.getQueue().size());
//不再添加新的任务了,但会把没执行完的继续执行完才关闭,不会阻塞主线程
threadPoolExecutor1.shutdown();
System.out.println("执行shutdown方法了,不会阻塞主线程");
结果
当前线程数>maximumPoolSize,并且使用的是SynchronousQueue队列的话,那么只会启动maximumPoolSize
个线程去执行任务(其中包含核心线程),对于超过最大线程数的线程,则抛出异常,不去处理
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 8, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor.getPoolSize());
System.out.println("A===" + threadPoolExecutor.getQueue().size());
Thread.sleep(3000);
System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor.getPoolSize());
System.out.println("B===" + threadPoolExecutor.getQueue().size());
结果
当LinkedBlockingDeque指定长度的时候(比如2),并且maximumPoolSize>=当前线程数>corePoolSize,首先启动核心线程数,然后把队列填满,剩下的(8-4-2)启动非核心线程
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2));
for (int i = 0; i < 8; i++) {
threadPoolExecutor.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor.getPoolSize());
System.out.println("A===" + threadPoolExecutor.getQueue().size());
Thread.sleep(3000);
System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor.getPoolSize());
System.out.println("B===" + threadPoolExecutor.getQueue().size());
结果
当LinkedBlockingDeque指定长度的时候,(当前任务数-核心线程数-队列长度)>(最大线程数-核心线程数【也就是最多能启动的非核心线程数】)时候,就会报错拒绝掉多余的任务,正在执行还有队列里等着的任务会继续执行
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2));
for (int i = 0; i < 12; i++) {
threadPoolExecutor.execute(runnable);
}
Thread.sleep(500);
System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
System.out.println("A===" + threadPoolExecutor.getPoolSize());
System.out.println("A===" + threadPoolExecutor.getQueue().size());
Thread.sleep(3000);
System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
System.out.println("B===" + threadPoolExecutor.getPoolSize());
System.out.println("B===" + threadPoolExecutor.getQueue().size());
结果
以上就是ThreadPoolExecutor的常见用法了。