线程池ThreadPoolExecutor使用踩坑

这个坑不是我挖的,我是无意间看到已离职同事的代码,这个代码是两年前写的了,声明的线程池方式如下:

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);
    }

希望大家引以为戒。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章