CountDownLatch踩过的坑

线上生产环境dubbo报线程池满了,经过一天排查锁定在开三个线程计算最后合并数据的步骤中。简单描述下该步骤线程开三个 调用三个不同的方法 使用countdownlatch 计数器等待三个方法全部执行完成 合并数据。但是由于其中一个方法调用第三方接口,接口返回异常导致转换数据报错。导致其中一个方法未正常完成。

举例demo:

public static void main(String[] args) {
    ExecutorService executorService =Executors.newFixedThreadPool(3);
    CountDownLatch cdl = new CountDownLatch(3);
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            /*try {
                function1();
            } catch (Exception e) {
                //异常处理
                e.printStackTrace();
            }
            finally {
                cdl.countDown();
            }*/
            function1();
        }
    });

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            function2();
            cdl.countDown();
        }
    });

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            function3();
            cdl.countDown();
        }
    });


    try {
        cdl.await();
        //cdl.await(20,TimeUnit.SECONDS);
        System.out.println("三个执行线程结束");
    } catch (InterruptedException e) {
        e.printStackTrace();
        System.out.println("执行线程异常");
    }
    finally {
        executorService.shutdown();
        System.out.println("执行线程关闭");
    }


}

private static void function1(){
    int i = 10/0;
    System.out.println("方法一");
}

private static void function2(){
    System.out.println("方法二");
}

private static void function3(){
    System.out.println("方法三");
}

方法一抛出异常,但是没有做异常处理导致不会执行线程关闭步骤,是不是和想象中不一样,一开始我也是懵,看了一下CountDownLatch原理就很好理解了,

“CountDownLatch是通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以恢复执行任务。”【1】

举一个现实中例子就是:CountDownLatch 就像跑步比赛中的裁判,三个方法就是就是三位运动员,运动员2,3都已经到达终点,但是运动员1摔倒了,动不了。裁判员只看到两位运动员到达终点不能宣布比赛结束,所以一直等。。。 

就像这样的场景导致线上service执行线程阻塞,接口调用次数累计导致dubbo线程满了(跟dubbo线程模型有关,有时间具体谈谈这一点)

知道原因了,就要考虑怎么修改

比赛不能无限期等,所以比赛必须在有限时间内结束,所以使用

public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

线程内部也许要增加异常处理

executorService.execute(new Runnable() {
    @Override
    public void run() {
        try {
            function1();
        } catch (Exception e) {
            //异常处理
            e.printStackTrace();
        }
        finally {
            cdl.countDown();
        }
       // function1();
    }
});

修改后demo

public static void main(String[] args) {
    ExecutorService executorService =Executors.newFixedThreadPool(3);
    CountDownLatch cdl = new CountDownLatch(3);
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            try {
                function1();
            } catch (Exception e) {
                //异常处理
                e.printStackTrace();
            }
            finally {
                cdl.countDown();
            }
           // function1();
        }
    });

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            function2();
            cdl.countDown();
        }
    });

    executorService.execute(new Runnable() {
        @Override
        public void run() {
            function3();
            cdl.countDown();
        }
    });


    try {
       // cdl.await();
        cdl.await(20,TimeUnit.SECONDS);
        System.out.println("三个执行线程结束");
    } catch (InterruptedException e) {
        e.printStackTrace();
        System.out.println("执行线程异常");
    }
    finally {
        executorService.shutdown();
        System.out.println("执行线程关闭");
    }


}

private static void function1(){
    int i = 10/0;
    System.out.println("方法一");
}

private static void function2(){
    System.out.println("方法二");
}

private static void function3(){
    System.out.println("方法三");
}

执行结果

大家结合自己的现实使用修改,爬过了使用坑,记录下分享下 ,希望能对别人有用

【1】参考https://www.jianshu.com/p/4b6fbdf5a08f

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章