03 线程池的拒绝策略

1 为什么要自定义线程池

  • FixedThreadPool 和 SingleThreadPool

允许的请求队列长度为 Integer.MAX_VALUE ,会堆积大量请求OOM

  • CachedThreadPool 和 ScheduledThreadPool

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量线程OOM

2 如何自定义线程池

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

3 自定义线程池规则

  • 线程池的线程数量长期维持在 corePoolSize 个(核心线程数量)
  • 线程池的线程数量最大可以扩展到 maximumPoolSize 个
  • 在 corePoolSize ~ maximumPoolSize 这个区间的线程,一旦空闲超过keepAliveTime时间,就会被杀掉(时间单位)
  • 送来工作的线程数量超过最大数以后,送到 workQueue 里面待业
  • 待业队伍也满了,就按照事先约定的策略 RejectedExecutionHandler 给拒绝掉

4 线程池的拒绝策略

所有拒绝策略都实现了接口 RejectedExecutionHandler

public interface RejectedExecutionHandler {
    /**
     * @param r  待执行任务
     * @param executor 线程池
     * @throws RejectedExecutionException  方法可能会抛出拒绝异常
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

4-1 AbortPolicy

直接抛出拒绝异常(继承自RuntimeException),会中断调用者的处理过程,所以除非有明确需求,一般不推荐

    public static class AbortPolicy implements RejectedExecutionHandler {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

4-2 CallerRunsPolicy

在调用者线程中(也就是说谁把 r 这个任务甩来的),运行当前被丢弃的任务。

只会用调用者所在线程来运行任务,也就是说任务不会进入线程池。

如果线程池已经被关闭,则直接丢弃该任务。

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
                r.run();
        }
    }
}

4-3 DiscardOledestPolicy

丢弃队列中最老的,然后再次尝试提交新任务。

    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                //获得待执行的任务队列,队列先进先出
                //poll()方法就能直接把队列中最老的抛弃掉,再次尝试执行execute(r)
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

BlockingQueue队列在线程池定义的时候就能看到,是一个阻塞队列

4-4 DiscardPolicy

默默丢弃无法加载的任务。

这个代码就很简单了,真的是啥也没做

    public static class DiscardPolicy implements RejectedExecutionHandler {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

4-5 通过实现 RejectedExecutionHandler 接口扩展

jdk内置的四种拒绝策略(都在ThreadPoolExecutor.java里面)代码都很简洁易懂。

我们只要继承接口都可以根据自己需要自定义拒绝策略。下面看两个例子。

一是netty自己实现的线程池里面私有的一个拒绝策略。单独启动一个新的临时线程来执行任务。

    private static final class NewThreadRunsPolicy implements RejectedExecutionHandler {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                final Thread t = new Thread(r, "Temporary task executor");
                t.start();
            } catch (Throwable e) {
                throw new RejectedExecutionException(
                        "Failed to start a new thread", e);
            }
        }
    }

另外一个是dubbo的一个例子,它直接继承的 AbortPolicy ,加强了日志输出,并且输出dump文件

public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        String msg = String.format("Thread pool is EXHAUSTED!" +
                        " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +
                        " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
                threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
                e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
                url.getProtocol(), url.getIp(), url.getPort());
        logger.warn(msg);
        dumpJStack();
        throw new RejectedExecutionException(msg);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值