CountDownLatch导致的线程阻塞问题及线程池的使用

CountDownLatch导致的线程阻塞问题及线程池的使用

CountDownLatch是什么

countdownlatch 是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完毕再执行。从命名可以解读到countdown 是倒数的意思,类似于我们倒计时的概念。countdownlatch 提供了两个方法,一个是 countDown,一个是 await,countdownlatch 初始化的时候需要传入一个整数,在这个整数倒数到 0 之前,调用了 await 方法的程序都必须要等待,然后通过countDown 来倒数。

项目中的应用

我们项目中有查询业绩功能,sql查询次数较多,于是使用多线程查询,但是查询结果需要统一封装返回,于是使用多线程查询的同时辅以CountDownLatch进行线程的约束

问题记录

查询响应较慢,每次响应时间大约都是10秒左右,经查看countDownLatch.await(10,TimeUnit.SECONDS)设定的阻塞时间为10秒,深入代码走查,发现新开的线程中有异常抛出而未捕获,导致线程计数器未按照理想状态下进行countDowm,最终过10秒线程主动向下执行导致的。

ThreadFactory threadFactory = Executors.defaultThreadFactory();
CountDownLatch countDownLatch = new CountDownLatch(4);
threadFactory.newThread(()->{
    Resps[0] = getDistributionPerformanceOfcurDay(req);
    countDownLatch.countDown();
}).start();
threadFactory.newThread(()->{
    Resps[1] = getDistributionPerformanceOfWeek(req);
    countDownLatch.countDown();
}).start();
threadFactory.newThread(()->{
    Resps[2] = getDistributionPerformanceOfMonth(req);
    countDownLatch.countDown();
}).start();
threadFactory.newThread(()->{
    Resps[3] = getDistributionPerformanceOfPreMonth(req);
    countDownLatch.countDown();
}).start();

上述每个线程中都是查询语句,分别查询当天、当周、当月以及上月的业绩。

在每个线程中都加上try-catch,finally中执行countDownLatch.countDown();解决!

学习下CountDownLatch以及线程池
jdk封装的线程池问题
  • FixedThreadPool 和 SingleThreadPool:
    允许的请求队列长度为 Integer.MAX_VALUE ,会堆积大量请求OOM
  • CachedThreadPool 和 ScheduledThreadPool:
    允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量线程OOM
创建线程池
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) 

虽然可以这么写,但是我可能需要在项目中使用很多个线程池,如果用的时候就新建,那岂不是跟new Thread的做法一样了?

所以自定义一个线程池,每次用这个线程池的时候保证只有一个,也就是搞成单例模式的,就是代码中可以随时取用,但是返回的永远是同一个。

public class MyThreadPool {

    private static volatile ThreadPoolExecutor threadPoolExecutor = null;

    public static ThreadPoolExecutor getThreadPool(){
        if(threadPoolExecutor != null) {
            return threadPoolExecutor;
        }
        synchronized (MyThreadPool.class){
            if(threadPoolExecutor == null){

                threadPoolExecutor = new ThreadPoolExecutor(10, 10, 300L,
                        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100),
                        new ThreadPoolRejectHandler());

                threadPoolExecutor.allowCoreThreadTimeOut(true); //任务执行完之后退出线程池
            }
        }
        return threadPoolExecutor;
    }
    public static class ThreadPoolRejectHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 可以直接抛异常,记录日志
            // 可以另外起一个临时线程处理这个被决绝的任务
        }
    }
}
  • 线程池的线程数量长期维持在 corePoolSize 个(核心线程数量)
  • 线程池的线程数量最大可以扩展到 maximumPoolSize 个
  • 在 corePoolSize ~ maximumPoolSize 这个区间的线程,一旦空闲超过keepAliveTime时间,就会被杀掉(时间单位)
  • 送来工作的线程数量超过最大数以后,送到 workQueue 里面等号
  • 等号的队列也满了,就按照事先约定的策略 RejectedExecutionHandler 给拒绝掉
  • 业务逻辑中调用时直接MyThreadPool.getThreadPool()即可
线程池的拒绝策略-默认4种

源码如下(加了注释),列出四种默认的拒绝策略

自己也可以定义个拒绝策略,实现RejectedExecutionHandler即可,然后在新建的线程池中加进去就行。我上面贴出来的自定义的线程池就是

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    /**
     *  在调用者线程中(也就是说是谁把任务放进线程池)运行当前被丢弃的任务。

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

	 *	如果线程池已经被关闭,则直接丢弃该任务。
     */
    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();
        }
    }
}

/**
 * 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());
    }
}

/**
 * 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);
        }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值