ThreadPoolExecutor 合理使用自带策略,避免线程被丢弃

本文通过案例展示了在使用ThreadPoolExecutor时遇到的RejectedExecutionException异常,解释了异常产生的原因是线程池和阻塞队列容量不足。文章详细介绍了RejectedExecutionHandler的四种策略,并提出了针对该问题的解决方案,即自定义线程池并设置适当的饱和策略。同时,作者不推荐直接使用Java内置的FixedThreadPool、SingleThreadPool、CachedThreadPool和ScheduledThreadPool,建议根据具体业务场景选择或定制线程池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 举个案例,复现异常:

public class ThreadPoolExecutorTest implements Runnable {
    int indexV = 0;

    public ThreadPoolExecutorTest(int index) {
        indexV = index;
    }

    public static void main(String[] args) {
        // 由于ArrayBlockingQueue内部只使用了一个锁来隔离读和写的操作,因此效率没有使用了两个锁来隔离读写操作的LinkedBlockingQueue高,故而不推荐使用ArrayBlockingQueue
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(10);// 无界队列,但是可以固定大小;
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES, queue);
        for (int index = 0; index < 100; index++) {
            try {
                poolExecutor.submit(new ThreadPoolExecutorTest(index));
            } catch (RejectedExecutionException e) {
                e.printStackTrace();
                System.out.println("线程池关闭后不允许向线程池中添加任务");
            }
        }
    }

    @Override
    public void run() {
        System.out.println("厉害咯,我的哥: " + indexV);

    }
}

执行以上代码,会报RejectedExecutionException异常,见文知义是某些线程被线程池执行器拒绝执行;

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@891d76 rejected from java.util.concurrent.ThreadPoolExecutor@121e5a[Running, pool size = 10, active threads = 7, queued tasks = 3, completed tasks = 14]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:110)
	at javabasic.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:51)

2.ThreadPoolExecutor 方法的参数定义:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

参数含义:

序号参数含义备注
1corePoolSize  核心线程数量,线程池初始化时设定corePoolSize 大小和 maximumPoolSize 大小一致的话 线程池中的线程将不会空闲、 keepAliveTime 和 timeUnit 就不会再起作用
2maximumPoolSize线程池最大线程数(非核心线程) 核心线程 和  非核心线程 共同使用线程池、但是核心线程是不会被回收的、回收条件是线程池中的线程数量大于核心线程数
3keepAliveTime  如果当前线程池中线程数大于corePoolSize。多余的线程、在等待keepAliveTime时间后如果还没有新的线程任务指派给它、它就会被回收
4unit   等待时间keepAliveTime的单位
5workQueue  等待队列,默认SynchronousQueue一个没有存储空间的阻塞队列 ,将任务同步交付给工作线程;
可以使用无界队LinkedBlockingQueue;
有界队列ArrayBlockingQueue;
以及优先级队列PriorityBlockingQueue 
6RejectedExecutionHandler饱和策略(分4中)AbortPolicy(默认):直接抛弃
CallerRunsPolicy:用调用者的线程执行任务(始终此种方式,可以避免线程被线程池拒绝的情况)
DiscardOldestPolicy:抛弃队列中最久的任务
DiscardPolicy:抛弃当前任务

 

3.异常产生的原因:

当线程池里的线程都繁忙的时候,新任务会被提交给阻塞队列保存,当提交给阻塞队列的任务,超出了该队列的最大容量时。线程池就会拒绝接收新任务,随即抛出异常;简单的理解为:队列的存储空间设置小了,但是实际业务中千万不要指着调整队列的大小来完全解决该问题;再往下看...

 

4. RejectedExecutionHandler的四种饱和策略:

序号策略名称含义备注
1AbortPolicy终止策略是默认的饱和策略,当队列满时,会抛出一个RejectExecutionException异常,客户可以捕获这个异常,根据需求编写自己的处理代码不是解决问题的根本
2DiscardPolicy策略会悄悄抛弃该任务。建议最好不用,水太深
3DiscardOldestPolicy策略将会抛弃下一个将要执行的任务,如果此策略配合优先队列PriorityBlockingQueue,该策略将会抛弃优先级最高的任务
4CallerRunsPolicy调用者运行策略,该策略不会抛出异常,不会抛弃任务,而是将任务回退给调用者线程执行(调用execute方法的线程),由于任务需要执行一段时间,所以在此期间不能提交任务,从而使工作线程有时间执行正在执行的任务。非常适合我们的业务场景

5. 解决办法,给定义的线程池加一个饱和策略:

 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES, queue, new CallerRunsPolicy());

 

6.为什么不建议使用FixedThreadPool,SingleThreadPool;CachedThreadPool,ScheduledThreadPool四种线程池:

序号线程池名称规则问题点
1FixedThreadPool允许的请求队列长度为 Integer.MAX_VALUE堆积大量的请求,从而导致 OOM
2SingleThreadPool 
3CachedThreadPool 允许的创建线程数量为 Integer.MAX_VALUE创建大量的线程,从而导致 OOM
4ScheduledThreadPool 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值