Java线程池(1)

昨天正好面试的时候,问了面试者线程池的问题,问题时这样的:”ThreadPoolExecutor中,假如corePoolSize=5, maximumPoolSize=10, workQueue的长度也是5,此时我向这个线程池中提交6个Runnable,线程池此时如何处理这第六个Runnable?”。

这道题目很多面试者容易犯错,因为直观上,我们感觉线程池应该这样处理:
(1) 当线程池中的线程数目小于maximumPoolSize时,新来一个任务的话,应该新创建一个线程来处理;
(2) 当线程池中存活的线程的数目达到了maximumPoolSize之后,新来的任务应该会被防到队列中,等在线程忙完之后再来处理。
实际情况是这样吗?先做一个实验:

    public class ThreadPoolTest {

    public static void main(String[] args) throws Exception{

        ExecutorService executorService = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
        List<Future> list = new ArrayList<Future>();

        //创建5个长任务,每个长任务会运行1分钟左右的时候
        int i = 5;
        while (i-- > 0) {
            list.add(executorService.submit(new LongRunnable()));
        }

        //提交一个短任务,段任务会打印它的运行时间
        list.add(executorService.submit(new ShortRunnable()));

        //打印当前提交时间
        System.out.println("submit time:" + new Date());

        //等待所有的任务执行完
        for(Future future : list) {
            future.get();
        }

        executorService.shutdown();

    }


    private static class ShortRunnable implements Runnable {

        @Override
        public void run() {
            System.out.println("run time: " + new Date());
        }
    }

    private static class LongRunnable implements Runnable {

        @Override
        public void run() {
            int i = 60;
            while (i-- > 0) {
                try {
                    Thread.sleep(1000L);
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }
    }

结果:

submit time:Tue Feb 23 15:05:28 CST 2016
run time: Tue Feb 23 15:06:28 CST 2016

从结果中可以看出,第六个提交的任务的运行时间比他的提交时间大概晚了1分钟左右,也就是说,当我们提交第六个任务的时候,线程池并没有创建一个新的线程来运行它,而是等最开始创建的5个线程中有线程空闲了才来运行第六个任务。这时候我们提交的这个任务线程池把它放在哪的呢?应该是放在队列中的。

下面我们修改测试代码, 把list.add(executorService.submit(new ShortRunnable()));修改成

       i = 6;
       while (i-- > 0) {
           list.add(executorService.submit(new ShortRunnable()));
       }

再运行下看看:
run time: Tue Feb 23 15:20:06 CST 2016
submit time:Tue Feb 23 15:20:06 CST 2016
run time: Tue Feb 23 15:20:06 CST 2016
run time: Tue Feb 23 15:20:06 CST 2016
run time: Tue Feb 23 15:20:06 CST 2016
run time: Tue Feb 23 15:20:06 CST 2016
run time: Tue Feb 23 15:20:06 CST 2016
此时我们发现, 提交的6个任务全部瞬间运行完了。这也说明了线程池的运行过程是这样的:
(1) 当提交一个任务交给线程池,而此时线程池中并没有空闲的线程来处理时, 线程池会把任务放在队列中缓存起来;
(2) 如果缓存队列满了,新提交一个任务时,此时线程池会判断池中的线程数目是否大于或等于maximumPoolSize, 如果没有大于或等于,则新创建一个线程来处理改任务。如果线程池中的线程数目已经大于或等于maximumPoolSize, 则拒绝服务。

我们看ThreadPoolExecutor的源代码:

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }

    private boolean addIfUnderCorePoolSize(Runnable firstTask) {
            Thread t = null;
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (poolSize < corePoolSize && runState == RUNNING)
                    t = addThread(firstTask);
            } finally {
                mainLock.unlock();
            }
            if (t == null)
                return false;
            t.start();
            return true;
        }

从代码中可以看出,
(1) 当线程池中添加任务时,线程池首先会检查当前存活的线程是否已经达到了corePoolSize;
(2) 如果未达到,则创建一个新的线程来处理该任务;
(3) 如果存活线程数目已达到corePoolSize,或者创建新的执行线程失败,则执行runState == RUNNING && workQueue.offer(command)
把任务添加到队列中。
(4) 我们知道阻塞队列的offer方法是不阻塞的, 也就是当队列满了之后,workQueue.offer(command)会返回false,此时, 则添加线程来执行任务addIfUnderMaximumPoolSize(command);
(5) 如果addIfUnderMaximumPoolSize(command)返回false, 则说明线程池中存活的线程数目已经达到了最大的线程数目, 则拒绝执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值