本文基于JDK1.8源码进行分析
当我们调用Executors.newFixedThreadPool(int nThreads )时会创建一个线程池给我. 源码这个方法的实现是:
1
2
3
4
5public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
那么我跟进去这个构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public 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();
这个类的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public 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, 这个策略就是忽略, 即你随意提交任务, 我不鸟你就完了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14public 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策略是说忽略最老的任务,然后执行我们提交的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public 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的实现先
1
2
3
4
5
6
7
8
9
10
11
12public 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异常,如果是其它的策略就要另当别论!面试是要阐述清楚!
最后
以上就是长情诺言最近收集整理的关于多线程:当你提交任务时,线程队列已经满了,这时会发生什么?的全部内容,更多相关多线程:当你提交任务时内容请搜索靠谱客的其他文章。
发表评论 取消回复