线程池拒绝策略简单理解
-
以下是 execute 执行时源码过程
/** * Executes the given task sometime in the future. The task * may execute in a new thread or in an existing pooled thread. * * 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 {@code RejectedExecutionHandler}. * * @param command the task to execute * @throws RejectedExecutionException at discretion of * {@code RejectedExecutionHandler}, if the task * cannot be accepted for execution * @throws NullPointerException if {@code command} is null */ public void execute(Runnable command) { if (command == null) throw new NullPointerException(); /* * Proceed in 3 steps: * 1.ctl 可以理解为当前线程状态 2.线程池中的线程数量小于核心线程数的代码逻辑 3.不满足 2 时,任务添加到队列的代码逻辑 4.阻塞队列逻辑以及拒绝策略的代码逻辑 */ 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); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }
-
以下是四种拒绝策略源码
AbortPolicy 丢弃任务并抛出RejectedExecutionException异常。 DiscardPolicy 丢弃任务,但是不抛出异常。 DiscardOldestPolicy 丢弃队列中最前面的任务,然后重新尝试执行任务。 CallerRunsPolicy 由调用线程处理该任务 (本人理解的是 会交给之前运行的线程去处理)。 public static class CallerRunsPolicy implements RejectedExecutionHandler { /** * Creates a {@code CallerRunsPolicy}. */ public CallerRunsPolicy() { } /** * Executes task r in the caller's thread, unless the executor * has been shut down, in which case the task is discarded. * 在调用者的线程中执行任务r,除非执行程序已经关闭 * 此方法说明 CallerRunsPolicy 不会丢失消息,会把消息 runnable 会等待上个线程执行完毕再去执行 * @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(); } } } /** * A handler for rejected tasks that throws a * {@code RejectedExecutionException}. */ public static class AbortPolicy implements RejectedExecutionHandler { /** * Creates an {@code AbortPolicy}. */ 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("Task " + r.toString() + " rejected from " + e.toString()); } } /** * A handler for rejected tasks that silently discards the * rejected task. */ public static class DiscardPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardPolicy}. */ 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) { } } /** * A handler for rejected tasks that discards the oldest unhandled * request and then retries {@code execute}, unless the executor * is shut down, in which case the task is discarded. */ public static class DiscardOldestPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardOldestPolicy} 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); } } }
-
下面我们尝试以下四种拒绝策略带来的效果
public class Consumer {
private final static Logger log = LoggerFactory.getLogger(Consumer.class);
public static void main(String[] args) throws InterruptedException {
int coreSize = 1;
int maxSize = 2;
ArrayBlockingQueue<Runnable> runnables = new ArrayBlockingQueue<Runnable>(5);
final ThreadPoolExecutor executor = new ThreadPoolExecutor(coreSize, maxSize, 1, TimeUnit.SECONDS, runnables, new ThreadPoolExecutor.AbortPolicy());
final Jedis jedis = AppContext.getInstance().getRedisPool();
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
executor.execute(new TaskThread("1"));
}
}
- AbortPolicy 阻塞队列跑满时会直接抛出异常信息
java.util.concurrent.RejectedExecutionException: Task TaskThread@79ca92b9 rejected from java.util.concurrent.ThreadPoolExecutor@1460a8c0[Running, pool size = 1, active threads = 1, queued tasks = 40, completed tasks = 41]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) ~[?:1.8.0_202]
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) ~[?:1.8.0_202]
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) ~[?:1.8.0_202]
at Consumer.main(Consumer.java:30) ~[classes/:?]
-
DiscardOldestPolicy || DiscardPolicy 并没有执行所有的任务
2023-04-08 22:29:31,214 INFO [main] AppContext:23 - redis 127.0.0.1:6379 connected 2023-04-08 22:29:31,217 INFO [pool-2-thread-1] Runnable:15 - received message 1 2023-04-08 22:29:31,217 INFO [pool-2-thread-2] Runnable:15 - received message 1 2023-04-08 22:29:31,217 INFO [pool-2-thread-1] Runnable:15 - received message 1 2023-04-08 22:29:31,217 INFO [pool-2-thread-2] Runnable:15 - received message 1 2023-04-08 22:29:31,217 INFO [pool-2-thread-1] Runnable:15 - received message 1 2023-04-08 22:29:31,217 INFO [pool-2-thread-2] Runnable:15 - received message 1 2023-04-08 22:29:31,217 INFO [pool-2-thread-1] Runnable:15 - received message 1
-
CallerRunsPolicy 可以执行所有的任务
2023-04-08 22:30:43,882 INFO [main] AppContext:23 - redis 127.0.0.1:6379 connected
2023-04-08 22:30:43,885 INFO [main] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [main] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [main] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [main] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,885 INFO [pool-2-thread-2] Runnable:15 - received message 1
2023-04-08 22:30:43,886 INFO [pool-2-thread-1] Runnable:15 - received message 1
2023-04-08 22:30:43,886 INFO [pool-2-thread-2] Runnable:15 - received message 1