线程池的四种拒绝策略实践

1 ThreadPoolExecutor.AbortPolicy

抛出 RejectedExecutionException来拒绝新任务的处理

//第一种拒绝策略
    //最大核心线程 1
    //最大线程 2
    //队列长度 1
    private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1,2,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1));

    public static void main(String[] args) throws InterruptedException {
        //一共放置四个线程

        //第一个线程交给核心线程执行
        threadPool.execute(()->{
            System.out.println("第一任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程到达最大值,第二个线程放进任务队列
        threadPool.execute(()->{
            System.out.println("第二任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,第三个线程交给新开的普通线程
        threadPool.execute(()->{
            System.out.println("第三任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,总线程数已满,执行拒绝策略,不去执行第四个任务,并抛出RejectedExecutionException异常
        threadPool.execute(()->{
            System.out.println("第四任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });


        Thread.sleep(100000);

        System.out.println("全部任务执行");
    }
Connected to the target VM, address: '127.0.0.1:57394', transport: 'socket'
第一任务开始执行
第三任务开始执行
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task test.ThreadTest.ThreadPool.RejectedExecution$$Lambda$4/716143810@1888ff2c rejected from java.util.concurrent.ThreadPoolExecutor@6adca536[Running, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at test.ThreadTest.ThreadPool.RejectedExecution.main(RejectedExecution.java:55)
第二任务开始执行

2 ThreadPoolExecutor.CallerRunsPolicy

多出来任务的先让main线程帮忙处理,main线程在处理期间提交的任务会阻塞在任务队列外

//第二种拒绝策略,可以扩展
    //最大核心线程 1
    //最大线程 2
    //队列长度 1
    private static ThreadPoolExecutor threadPool1 = new ThreadPoolExecutor(1,2,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),new ThreadPoolExecutor.CallerRunsPolicy());


    public static void main(String[] args) throws InterruptedException {
        //一共放置四个线程

        //第一个线程交给核心线程执行
        threadPool1.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第一任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程到达最大值,第二个线程放进任务队列
        threadPool1.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第二任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,第三个线程交给新开的普通线程
        threadPool1.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第三任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,总线程数已满,执行拒绝策略,使用主线程main执行
        threadPool1.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第四任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        //我们不知道 第五条干啥去了 初步推测在队列外等着,直到第二任务被拿出,第五任务放入队列
        threadPool1.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第五任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });



        Thread.sleep(100000);

        System.out.println("全部任务执行");
    }
Connected to the target VM, address: '127.0.0.1:57525', transport: 'socket'
pool-2-thread-1第一任务开始执行
pool-2-thread-2第三任务开始执行
main第四任务开始执行
pool-2-thread-1第二任务开始执行
pool-2-thread-2第五任务开始执行

3 ThreadPoolExecutor.DiscardPolicy

直接丢弃

  //第三种拒绝策略,直接抛弃掉
    //最大核心线程 1
    //最大线程 2
    //队列长度 1
    private static ThreadPoolExecutor threadPool2 = new ThreadPoolExecutor(1,2,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),new ThreadPoolExecutor.DiscardPolicy());


    public static void main(String[] args) throws InterruptedException {
        //一共放置四个线程

        //第一个线程交给核心线程执行
        threadPool2.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第一任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程到达最大值,第二个线程放进任务队列
        threadPool2.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第二任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,第三个线程交给新开的普通线程
        threadPool2.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第三任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,总线程数已满,执行拒绝策略,直接抛弃
        threadPool2.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第四任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        Thread.sleep(100000);

        System.out.println("全部任务执行完成!");
    }
Connected to the target VM, address: '127.0.0.1:57607', transport: 'socket'
pool-3-thread-1第一任务开始执行
pool-3-thread-2第三任务开始执行
pool-3-thread-1第二任务开始执行

4 ThreadPoolExecutor.DiscardOldestPolicy

把最早未处理的任务丢弃

  //第四种拒绝策略,抛弃掉队列中最早未处理的任务需求
    //最大核心线程 1
    //最大线程 2
    //队列长度 1
    private static ThreadPoolExecutor threadPool3 = new ThreadPoolExecutor(1,2,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),new ThreadPoolExecutor.DiscardOldestPolicy());


    public static void main(String[] args) throws InterruptedException {
        //一共放置四个线程

        //第一个线程交给核心线程执行
        threadPool3.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第一任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程到达最大值,第二个线程放进任务队列
        threadPool3.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第二任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,第三个线程交给新开的普通线程
        threadPool3.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第三任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        //因为核心线程已满,任务队列已满,总线程数已满,执行拒绝策略,抛弃已经在队列中的第二任务,将第四任务加入任务队列
        threadPool3.execute(()->{
            System.out.println(Thread.currentThread().getName()+"第四任务开始执行");
            try {
                //睡10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        Thread.sleep(100000);

        System.out.println("全部任务执行完成!");
    }
Connected to the target VM, address: '127.0.0.1:57682', transport: 'socket'
pool-4-thread-1第一任务开始执行
pool-4-thread-2第三任务开始执行
pool-4-thread-2第四任务开始执行
### Java线程池拒绝策略概述 Java线程池提供了多种内置的拒绝策略来处理任务提交超出线程池容量的情况。这些策略可以通过`ThreadPoolExecutor`类进行设置,具体实现依赖于`RejectedExecutionHandler`接口。 #### 内置拒绝策略详解 1. **AbortPolicy** 默认的拒绝策略,在无法执行新任务时会抛出`RejectedExecutionException`异常[^4]。这种方式适用于希望程序立即失败并报告错误的场景。 2. **CallerRunsPolicy** 当任务被拒绝时,该策略会让提交任务的线程(即调用者线程)执行这个任务。这可能会降低系统的整体吞吐量,但在某些情况下可以缓解压力。 3. **DiscardPolicy** 静默丢弃被拒绝的任务而不做任何通知或记录。这种策略适合那些允许丢失部分任务的应用场景。 4. **DiscardOldestPolicy** 试图丢弃最旧的一个请求以便为当前任务腾出空间。此策略可能会影响较早提交的任务完成时间。 #### 自定义拒绝策略 除了上述四种标准策略外,还可以通过继承`RejectedExecutionHandler`接口来自定义拒绝逻辑。例如,`AbortPolicyWithReport`扩展了默认的`AbortPolicy`,可以在拒绝任务的同时提供额外的日志或其他操作支持[^2]。 以下是创建自定义拒绝策略的一个简单例子: ```java public class CustomRejectPolicy implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.err.println("Task " + r.toString() + " rejected from " + executor.toString()); // 可在此处添加其他处理逻辑,比如保存到队列或者日志记录 } } ``` #### 设置拒绝策略的方法 要在线程池中应用特定的拒绝策略,需在初始化`ThreadPoolExecutor`实例时指定它。下面是一个完整的示例代码片段展示如何配置带有不同拒绝策略线程池: ```java import java.util.concurrent.*; public class ThreadPoolExample { public static void main(String[] args) { int corePoolSize = 2; int maximumPoolSize = 4; long keepAliveTime = 10L; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2); // 创建具有CallerRunsPolicy拒绝策略线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new ThreadPoolExecutor.CallerRunsPolicy() ); // 提交超过线程池承载能力的任务数测试效果 for (int i = 0; i < 10; i++) { final int taskNumber = i; threadPool.submit(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Executing Task " + taskNumber); }); } threadPool.shutdown(); } } ``` #### 最佳实践建议 为了更高效地利用线程池资源并减少因拒绝策略引发的问题,应遵循以下几点最佳实践: - 合理设定核心线程数(`corePoolSize`)和最大线程数(`maximumPoolSize`)以匹配实际负载需求[^3]。 - 调整阻塞队列大小(`workQueue`)以平衡内存占用与响应速度之间的关系。 - 定期监控线程池运行状况,包括活动线程计数、已完成任务总数等指标,并据此动态调整参数。 - 根据业务特点选择合适的拒绝策略;如果需要更高的灵活性,则考虑开发定制化解决方案。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值