多线程:当你提交任务时,线程队列已经满了,这时会发生什么?

本文基于JDK1.8源码进行分析
当我们调用Executors.newFixedThreadPool(int nThreads )时会创建一个线程池给我. 源码这个方法的实现是:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }


那么我跟进去这个构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
 
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Ok,当我看到这里的时候, 我已经看到我想要的了.RejectedExecutionHandler这个是拒绝执行者. 那么我看看这个我传进去的defaultHandler是什么, 它就是终止策略
private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
这个类的实现

public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an <tt>AbortPolicy</tt>.
         */
        public AbortPolicy() { }
 
        /**
         * Always throws RejectedExecutionException.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always.
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException();
        }
    }

直接抛这个异常出去. 那么我明白了, 就是因为我的线程池的拒绝策略是AbortPolicy,所以会导致抛异常!  好, 那么现在我知道为什么抛异常了, 是不是就够了呢? 一般来说用来解决问题是够了, 但是我还想研究下什么情况下会抛这个异常, 和它的终极解决方案!

我们先来看看线程池的四种拒绝策略:

 注: 当线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。注:默认策略!!!!!!

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
 

1:解决方案的探索非常简单,  无非就是RejectedExecutionHandler嘛, 我Ctrl+T看看他有什么实现类就Ok了嘛,  它有四个实现类.AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy. 

关于AbortPolicy我刚才已经说了,它的拒绝策略就是抛异常. 说说下面三个.

CallerRunsPolicy这个的拒绝策略是如果线程池没有shutDown,就会执行需要执行的Runnable

 

public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a <tt>CallerRunsPolicy</tt>.
         */
        public CallerRunsPolicy() { }
 
        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

所以,我们解决抛异常的就直接用CallerRunsPolicy这个策略就可以了.
再来DiscardPolicy, 这个策略就是忽略, 即你随意提交任务, 我不鸟你就完了.

public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a <tt>DiscardPolicy</tt>.
         */
        public DiscardPolicy() { }
 
        /**
         * Does nothing, which has the effect of discarding task r.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }


DiscardOldestPolicy策略是说忽略最老的任务,然后执行我们提交的.

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a <tt>DiscardOldestPolicy</tt> for the given executor.
         */
        public DiscardOldestPolicy() { }
 
        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

从实现也可以看出来, 把队列里面顶部的弹掉, 再执行我们的任务
大家可以根据不同所需, 在创建线程池之后, 用你们的ThreadPoolExecutor.setRejectedExecutionHandler(RejectedExecutionHandler handler)去设置你们的拒绝策略

2:那么本文还要继续探索何时这些拒绝策略会被调用呢? 我以ThreadPoolExecutor的execute()方法说事了, ScheduledThreadPoolExecutor的submit方法是大同小异的,请大家自己跟代码去吧.

execute方法的javadoc中有这么一句话:

If the task cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been reached,
the task is handled by the current RejectedExecutionHandler

看看execute的实现先

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


这里一共涉及到三个方法,addIfUnderCorePoolSize,ensureQueuedTaskHandled,addIfUnderMaximumPoolSize
只要执行到reject(command)这里就会调用我们那个Handler的rejectedExecution()方法.

那三个方法以及javadoc都告诉我们,  如果这个线程池已经shutdown或者容量满了之后, 就会调用拒绝策略.. 请注意,. 从实现来看,  这个容量满了是指当前的线程池以及其缓冲队列的容量都满了 才是满了, 而不是线程池的数量。

既然提到阻塞队列已满说明不是LinkedBlockingQueue!因为LinkedBlockingQueue是近似无界的!

由于默认是AbortPolicy拒绝策略,因此极可能是抛出RejectedExecutionException异常,如果是其它的策略就要另当别论!面试是要阐述清楚!
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值