java ThreadPoolExecutor踩坑记

首先简单介绍下ThreadPoolExecutor的原理,如图:


java ThreadPoolExecutor踩坑记_第1张图片
image.png

每当有新任务需要提交到线程池执行的时候,大概的过程如下:

  • step1.调用ThreadPoolExecutor的execute提交线程,首先检查CorePool,如果CorePool内的线程小于CorePoolSize,新创建线程执行任务。
  • step2.如果当前CorePool内的线程大于等于CorePoolSize,那么将任务加入到BlockingQueue。
  • step3.如果不能加入BlockingQueue,在小于MaxPoolSize的情况下创建线程执行任务。
  • step4.如果线程数大于等于MaxPoolSize,那么执行拒绝策略。

其中1,2,4步骤的机制原来也就知道,只是对于step3原先关注的不多,这次踩到了。本意是想让超出MaxPoolSize的时候,任务能执行在父线程里执行,所以想当然的将BlockingQueue的大小设置为1了。结果出现的现象是,在并发很低的情况下,出现了任务被线程池拒绝的情况。调试了一下午,才发现卡在了step3,源代码如下:

       int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        } 
       // step 3 workQueue.offer(command))
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

关键点就在workQueue.offer(command))这句:如果当前线程数已经大于coreSize了,则会先尝试往对列里放。如果队列已经满了,则直接看能不能再建线程,如果还不能则走拒绝策略。这个时候,其实coreSize的线程可能都是空闲的,他们是在等待workQueue里有新的任务出现。而新的任务(特别是一次性批量提交大于1个的时候),因为BlockingQueue size是1,所以只有1个能提交成功,其他的都被拒绝了。
想想也是啊,这就是一个生产者消费者模式,线程被创建出来之后,和后续提交的任务之间唯一媒介就是workQueue!!

更详细的关于ThreadPoolExecutor的机制可以参考:
http://www.jianshu.com/p/ade771d2c9c0

你可能感兴趣的:(java ThreadPoolExecutor踩坑记)