这个坑不是我挖的,我是无意间看到已离职同事的代码,这个代码是两年前写的了,声明的线程池方式如下:
private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0,
100, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
如果你能一眼看出问题,那就不需要往下看了。
坑就出在这个五个参数上,依次如下:
corePoolSize(核心线程池): 0
maximumPoolSize(最大线程池): 100
keepAliveTime(空闲线程保留时间): 60
timeUnit(时间单位): 秒
workQueue(阻塞队列): LinkedBlockingQueue
虽然上面的corePoolSize设置为0,但是至少会有一个线程,所以在实际运行时线程池中会有一个线程。
按上面这个方式初始化的线程池,线程池里面永远只会有一个线程在处理。
因为当线程数量达到corePoolSize后,阻塞队列被占满后才会继续创建线程(线程数<maximumPoolSize),上面阻塞队列采用的是无界对列LinkedBlockingQueue,不可能占满,所以maximumPoolSize参数也相当于无效了。
测试程序:
public class ThreadPoolTest {
private static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<30;i++) {
Thread t = new Thread(()-> {
try {
TimeUnit.SECONDS.sleep(5);
System.out.println("threadName:"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
poolExecutor.execute(t);
}
}
}
永远只会有三个工作线程,输出结果:
threadName:pool-1-thread-1
threadName:pool-1-thread-2
threadName:pool-1-thread-3
threadName:pool-1-thread-3
threadName:pool-1-thread-2
threadName:pool-1-thread-1
threadName:pool-1-thread-3
threadName:pool-1-thread-1
当把阻塞队列的大小设置为20,任务把阻塞队列占满,然后增加工作线程,工作线程才能达到10个。
下面是ThreadPoolExecutor.execute()方法源码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 小于核心线程池时直接添加线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 线程是运行状态 && 添加阻塞队列成功
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 再次检查线程池状态,不是运行状态就删除拒绝任务
if (! isRunning(recheck) && remove(command))
reject(command);
// 再次 判断当前线程数量是否等于0,等于0就新增加任务(一般都不满足)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
希望大家引以为戒。