文章目录
Java 线程池 ThreadPoolExecutor 拒绝策略
线程池参数
- corePoolSize:线程池中的常驻核心线程数。
- maximumPoolSize:线程池允许的最大线程数。
- keepAliveTime:当线程数大于核心线程数时,多余的空闲线程等待新任务的最大时间。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于在任务太多时存储多余的任务。
- threadFactory:创建线程的工厂,可以设置线程的名字和优先级等。
- handler:拒绝策略,当任务太多且队列也满时,如何处理新任务。
1. AbortPolicy(中止策略 - 默认策略)
说明: 不执行新的任务并抛出RejectedExecutionException异常。
应用场景:适用于对系统的稳定性要求较高,不希望丢失任务,但希望能快速发现任务提交失败的情况。
/**
* A handler for rejected tasks that throws a
* {@link RejectedExecutionException}.
*
* This is the default handler for {@link ThreadPoolExecutor} and
* {@link ScheduledThreadPoolExecutor}.
*/
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());
}
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolAbortPolicy {
private static final AtomicInteger TICKETS = new AtomicInteger(30);
public static void main(String[] args) {
try (
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
0L, // 空闲时间
TimeUnit.SECONDS, // 空闲时间单位
new LinkedBlockingQueue<>(10), // 阻塞队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略
)
) {
for (int i = 0; i < 30; i++) {
threadPoolExecutor.execute(() -> {
if (TICKETS.get() > 0) {
System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement());
}
});
}
}
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
/*
总共可以售票15张, 因为设置最大线程数为5, 且队列最大的数量为10, 剩下的因为队列已经满了, 所以会被拒绝抛出 RejectedExecutionException.
===> 售票员: pool-1-thread-4, 售出票号为: 27
===> 售票员: pool-1-thread-5, 售出票号为: 26
===> 售票员: pool-1-thread-3, 售出票号为: 28
===> 售票员: pool-1-thread-1, 售出票号为: 30
===> 售票员: pool-1-thread-2, 售出票号为: 29
===> 售票员: pool-1-thread-4, 售出票号为: 25
===> 售票员: pool-1-thread-5, 售出票号为: 24
===> 售票员: pool-1-thread-3, 售出票号为: 23
===> 售票员: pool-1-thread-1, 售出票号为: 22
===> 售票员: pool-1-thread-2, 售出票号为: 21
===> 售票员: pool-1-thread-4, 售出票号为: 20
===> 售票员: pool-1-thread-5, 售出票号为: 19
===> 售票员: pool-1-thread-3, 售出票号为: 18
===> 售票员: pool-1-thread-1, 售出票号为: 17
===> 售票员: pool-1-thread-2, 售出票号为: 16
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.apollo.pool.ThreadPoolAbortPolicy$$Lambda$14/0x00000008010029f8@9f70c54 rejected from java.util.concurrent.ThreadPoolExecutor@1e67b872[Running, pool size = 5, active threads = 5, queued tasks = 10, completed tasks = 0]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2081)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:841)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1376)
at com.apollo.pool.ThreadPoolAbortPolicy.main(ThreadPoolAbortPolicy.java:26)
*/
2. CallerRunsPolicy(调用者运行策略)
说明: 当任务被拒绝添加时,如果调用者所在的线程不是线程池中的线程,那么任务将由该调用者所在的线程直接执行。
**应用场景:**适用于系统不希望丢弃任务,并且对任务的执行延迟要求不高的情况。当线程池已经饱和时,新任务会在提交任务的线程中执行,可能会导致提交任务的线程阻塞。
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
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.
*
* @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();
}
}
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolCallerRunsPolicy {
private static final AtomicInteger TICKETS = new AtomicInteger(30);
public static void main(String[] args) {
try (
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
)
) {
for (int i = 0; i < 30; i++) {
executor.execute(() ->
System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement())
);
}
}
try {
Thread.sleep(6000);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
/*
===> 售票员: pool-1-thread-1, 售出票号为: 30
===> 售票员: main, 售出票号为: 29
===> 售票员: pool-1-thread-4, 售出票号为: 26
===> 售票员: pool-1-thread-3, 售出票号为: 27
===> 售票员: pool-1-thread-5, 售出票号为: 25
===> 售票员: pool-1-thread-2, 售出票号为: 28
===> 售票员: pool-1-thread-1, 售出票号为: 24
===> 售票员: main, 售出票号为: 23
===> 售票员: pool-1-thread-4, 售出票号为: 22
===> 售票员: pool-1-thread-3, 售出票号为: 21
===> 售票员: pool-1-thread-5, 售出票号为: 20
===> 售票员: pool-1-thread-2, 售出票号为: 19
===> 售票员: pool-1-thread-1, 售出票号为: 18
===> 售票员: main, 售出票号为: 17
===> 售票员: pool-1-thread-4, 售出票号为: 16
===> 售票员: pool-1-thread-3, 售出票号为: 15
===> 售票员: pool-1-thread-5, 售出票号为: 14
===> 售票员: pool-1-thread-5, 售出票号为: 8
===> 售票员: pool-1-thread-2, 售出票号为: 13
===> 售票员: pool-1-thread-1, 售出票号为: 12
===> 售票员: main, 售出票号为: 11
===> 售票员: pool-1-thread-4, 售出票号为: 10
===> 售票员: pool-1-thread-3, 售出票号为: 9
===> 售票员: pool-1-thread-5, 售出票号为: 7
===> 售票员: pool-1-thread-2, 售出票号为: 6
===> 售票员: pool-1-thread-1, 售出票号为: 5
===> 售票员: pool-1-thread-4, 售出票号为: 4
===> 售票员: pool-1-thread-3, 售出票号为: 3
===> 售票员: pool-1-thread-5, 售出票号为: 2
===> 售票员: pool-1-thread-2, 售出票号为: 1
*/
3. DiscardPolicy(丢弃策略)
说明:默默丢弃无法处理的任务,不抛出异常。
应用场景:适用于可以容忍任务丢失的情况,例如不重要的日志处理任务。
/**
* 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) {
}
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Function: 线程池拒绝策略 - DiscardPolicy 丢弃任务策略
*/
public class ThreadPoolDiscardPolicy {
private static final AtomicInteger TICKETS = new AtomicInteger(30);
public static void main(String[] args) {
try (
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy()
)
) {
for (int i = 0; i < 30; i++) {
executor.execute(() ->
System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement())
);
}
}
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
/*
===> 售票员: pool-1-thread-5, 售出票号为: 26
===> 售票员: pool-1-thread-4, 售出票号为: 27
===> 售票员: pool-1-thread-5, 售出票号为: 25
===> 售票员: pool-1-thread-4, 售出票号为: 24
===> 售票员: pool-1-thread-3, 售出票号为: 28
===> 售票员: pool-1-thread-5, 售出票号为: 23
===> 售票员: pool-1-thread-1, 售出票号为: 30
===> 售票员: pool-1-thread-2, 售出票号为: 29
===> 售票员: pool-1-thread-4, 售出票号为: 22
===> 售票员: pool-1-thread-3, 售出票号为: 21
===> 售票员: pool-1-thread-5, 售出票号为: 20
===> 售票员: pool-1-thread-1, 售出票号为: 19
===> 售票员: pool-1-thread-2, 售出票号为: 18
===> 售票员: pool-1-thread-4, 售出票号为: 17
===> 售票员: pool-1-thread-3, 售出票号为: 16
*/
4. DiscardOldestPolicy(丢弃最旧策略)
说明:丢弃任务队列中最旧的未处理任务,然后将新任务加入队列重新尝试执行。
应用场景:适用于任务的重要性相对较低,且希望尽量处理新提交的任务的情况。
/**
* 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. This policy is
* rarely useful in cases where other threads may be waiting for
* tasks to terminate, or failures must be recorded. Instead consider
* using a handler of the form:
* <pre> {@code
* new RejectedExecutionHandler() {
* public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
* Runnable dropped = e.getQueue().poll();
* if (dropped instanceof Future<?>) {
* ((Future<?>)dropped).cancel(false);
* // also consider logging the failure
* }
* e.execute(r); // retry
* }}}</pre>
*/
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);
}
}
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Function: 线程池拒绝策略 - DiscardOldestPolicy 丢弃最老的任务策略
*/
public class ThreadPoolDiscardOldestPolicy {
private static final AtomicInteger TICKETS = new AtomicInteger(30);
public static void main(String[] args) {
try (
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
)
) {
for (int i = 0; i < 30; i++) {
executor.execute(() ->
System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement())
);
}
}
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
/*
===> 售票员: pool-1-thread-2, 售出票号为: 29
===> 售票员: pool-1-thread-5, 售出票号为: 26
===> 售票员: pool-1-thread-1, 售出票号为: 30
===> 售票员: pool-1-thread-4, 售出票号为: 27
===> 售票员: pool-1-thread-3, 售出票号为: 28
===> 售票员: pool-1-thread-2, 售出票号为: 25
===> 售票员: pool-1-thread-5, 售出票号为: 24
===> 售票员: pool-1-thread-1, 售出票号为: 23
===> 售票员: pool-1-thread-4, 售出票号为: 22
===> 售票员: pool-1-thread-3, 售出票号为: 21
===> 售票员: pool-1-thread-2, 售出票号为: 20
===> 售票员: pool-1-thread-5, 售出票号为: 19
===> 售票员: pool-1-thread-1, 售出票号为: 18
===> 售票员: pool-1-thread-4, 售出票号为: 17
===> 售票员: pool-1-thread-3, 售出票号为: 16
*/
5. 自定义拒绝策略
说明: 需要实现接口 RejectedExecutionHandler, 重写 void rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法.
package com.apollo.pool;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolSelfDefinePolicy {
private static final AtomicInteger TICKETS = new AtomicInteger(30);
public static void main(String[] args) {
try (
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new SelfDefinePolicy()
)
) {
for (int i = 0; i < 30; i++) {
executor.execute(() -> {
if (TICKETS.get() > 0) {
System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement());
}
});
}
}
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
static class SelfDefinePolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 记录拒绝的任务
System.out.println("===> 自定义拒绝策略: Task " + r.toString() + " rejected.");
// 抛出自定义的异常
throw new RejectedExecutionException("Task " + r + " rejected.");
}
}
}
/*
===> 自定义拒绝策略: Task com.apollo.pool.ThreadPoolSelfDefinePolicy$$Lambda$14/0x0000000801002c10@9f70c54 rejected.
===> 售票员: pool-1-thread-2, 售出票号为: 29
===> 售票员: pool-1-thread-1, 售出票号为: 30
===> 售票员: pool-1-thread-3, 售出票号为: 28
===> 售票员: pool-1-thread-5, 售出票号为: 27
===> 售票员: pool-1-thread-4, 售出票号为: 26
===> 售票员: pool-1-thread-2, 售出票号为: 25
===> 售票员: pool-1-thread-1, 售出票号为: 24
===> 售票员: pool-1-thread-3, 售出票号为: 23
===> 售票员: pool-1-thread-5, 售出票号为: 22
===> 售票员: pool-1-thread-4, 售出票号为: 21
===> 售票员: pool-1-thread-2, 售出票号为: 20
===> 售票员: pool-1-thread-1, 售出票号为: 19
===> 售票员: pool-1-thread-3, 售出票号为: 18
===> 售票员: pool-1-thread-5, 售出票号为: 17
===> 售票员: pool-1-thread-4, 售出票号为: 16
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.apollo.pool.ThreadPoolSelfDefinePolicy$$Lambda$14/0x0000000801002c10@9f70c54 rejected.
at com.apollo.pool.ThreadPoolSelfDefinePolicy$SelfDefinePolicy.rejectedExecution(ThreadPoolSelfDefinePolicy.java:56)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:841)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1376)
at com.apollo.pool.ThreadPoolSelfDefinePolicy.main(ThreadPoolSelfDefinePolicy.java:30)
*/

922

被折叠的 条评论
为什么被折叠?



