线程池环境下,慎用循环栅栏CyclicBarrie

背景描述

最近写业务代码时,意外地发现,当CyclicBarrie预定的拦截线程数超过线程池核心数时,由线程池开启且调用了CyclicBarrie对象await()方法的子线程,将一直陷入等待。

简单来说就是,

当new CyclicBarrie(int parties)的parties参数大于线程池的corePoolSize就会出现如上情况。


场景复现

  线程池工具类代码

public class ThreadPoolUtil {

    private static ExecutorService executorService;

    static {
        // 初始化加载
        initExecuteService();
    }

    private static void initExecuteService() {
        try {
            Properties properties = new Properties();
            properties.load(ThreadPoolUtil.class.getClassLoader().getResourceAsStream("threadPool.properties"));
            if (properties == null) {
                throw new RuntimeException("threadPool参数读取异常:配置文件无法加载");
            }
//            int corePoolSize = Integer.parseInt((String)properties.get("thread.pool.corePoolSize"));
//            int maxPoolSize = Integer.parseInt((String)properties.get("thread.pool.maxPoolSize"));
            // 直接获取JVM可用核心数
            int corePoolSize = Runtime.getRuntime().availableProcessors();
            int maxPoolSize = 2 * corePoolSize;
            // 非核心线程最大空闲生存时间
            long keepAliveSeconds = Long.parseLong((String) properties.get("thread.pool.keepAliveSeconds"));
            int queueCapacity = Integer.parseInt((String) properties.get("thread.pool.queueCapacity"));
            String threadFactoryName = (String) properties.get("thread.pool.threadFactoryName");
            // 线程池创建
            executorService = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(queueCapacity), new MyThreadFactory(threadFactoryName));
        } catch (IOException var1) {
            throw new RuntimeException("threadPool参数读取异常:加载配置文件threadPool.properties失败", var1);
        }
    }

    public static ExecutorService getExecutorService() {
        if (executorService == null) {
            initExecuteService();
        }
        return executorService;
    }

}

ps:线程池核心数采用JVM可用核心数 

// 本人本机JVM可用线程数为4
int corePoolSize = Runtime.getRuntime().availableProcessors();

 业务实现代码 

public class CyclicBarrierTest01 {

    private final static Logger LOGGER = LoggerFactory.getLogger(CyclicBarrierTest01.class);

    public static void main(String[] args) {
        int threadNum = 2;
        LOGGER.info("JVM可用核心数【{}】,demo即将启用线程数【{}】", Runtime.getRuntime().availableProcessors(), threadNum);
        // 栅栏预定threadNum个线程数量,等待线程达到threadNum个后将执行此runnable
        CyclicBarrier cyclicBarrier = new CyclicBarrier(threadNum, ()-> LOGGER.info("等待线程已满足{}个,放行", threadNum));

        ExecutorService executorService = ThreadPoolUtil.getExecutorService();
        for(int i=0;i<threadNum;i++){
            final int j = i+1;
            executorService.execute(()->{
                try {
                    LOGGER.info("从线程[{}]正在执行逻辑...1", Thread.currentThread().getName());
                    Thread.sleep(j*1000);
                    // 当前线程等待其他子线程到达栅栏
                    cyclicBarrier.await();
                    LOGGER.info("从线程[{}]正在执行逻辑...2", Thread.currentThread().getName());
                    Thread.sleep(j*1000);
                    // 当前线程等待其他子线程到达栅栏
                    cyclicBarrier.await();
                    LOGGER.info("从线程[{}]正在执行逻辑...3", Thread.currentThread().getName());
                    // 当前线程等待其他子线程到达栅栏
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    LOGGER.error("睡眠异常", e);
                } catch (BrokenBarrierException e) {
                    LOGGER.error("栅栏已被冲破", e);
                }
            });
        }
        executorService.shutdown();
    }

}

业务实现代码中,开启线程数暂时定为2,本机JVM可用核心数为4,

此时new CyclicBarrier(2),小于线程池核心数(本机JVM可用核心数),模拟正常执行。

启动main方法,查看结果如下:

16:56:05.651 [main] INFO CyclicBarrierTest01  - JVM可用核心数【4】,demo即将启用线程数【2】
16:56:05.766 [shangHaiPool-1-thread-1] INFO CyclicBarrierTest01  - 从线程[shangHaiPool-1-thread-1]正在执行逻辑...1
16:56:05.767 [shangHaiPool-1-thread-2] INFO CyclicBarrierTest01  - 从线程[shangHaiPool-1-thread-2]正在执行逻辑...1
16:56:07.780 [shangHaiPool-1-thread-2] INFO CyclicBarrierTest01 - 等待线程已满足2个,放行
16:56:07.780 [shangHaiPool-1-thread-1] INFO CyclicBarrierTest01  - 从线程[shangHaiPool-1-thread-1]正在执行逻辑...2
16:56:07.780 [shangHaiPool-1-thread-2] INFO CyclicBarrierTest01  - 从线程[shangHaiPool-1-thread-2]正在执行逻辑...2
16:56:09.781 [shangHaiPool-1-thread-2] INFO CyclicBarrierTest01  - 等待线程已满足2个,放行
16:56:09.782 [shangHaiPool-1-thread-1] INFO CyclicBarrierTest01  - 从线程[shangHaiPool-1-thread-1]正在执行逻辑...3
16:56:09.782 [shangHaiPool-1-thread-2] INFO CyclicBarrierTest01  - 从线程[shangHaiPool-1-thread-2]正在执行逻辑...3
16:56:09.782 [shangHaiPool-1-thread-2] INFO CyclicBarrierTest01  - 等待线程已满足2个,放行

程序正常执行,此时将threadNum修改为5,本机JVM可用核心数为4,

此时new CyclicBarrier(5),大于线程池核心数(本机JVM可用核心数),模拟异常执行。

int threadNum = 5;
LOGGER.info("JVM可用核心数【{}】,demo即将启用线程数【{}】", Runtime.getRuntime().availableProcessors(), threadNum);

启动main方法,查看结果如下:

 此时,子线程全部陷入等待,线程池由于存在任务未结束则不会被资源关闭。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值