CountDownLatch怎么使用?一个简单的Demo与使用注意事项

前言

在工作中,大家应该比较少用到CountDownLatch,但是CountDownLatch在面试中或者多线程的相关知识点面试中还是经常被问到,所以这次跟大家复习一下CountDownLatch的简单使用。

 

什么是CountDownLatch(线程计数器 )

CountDownLatch 类位于 java.util.concurrent 包下,利用它可以实现类似计数器的功能。比如有

一个任务 A,它要等待其他 2 个任务执行完毕之后才能执行,此时就可以利用 CountDownLatch

来实现这种功能了。

 

 

怎么使用CountDownLatch

以下讲解我会先放上正确的使用Demo,后面再讲一些理解的地方和错误的使用:

 

public class Test {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main方法开始");
        int n = 2;
        final CountDownLatch countDownLatch = new CountDownLatch(n);
        Executor executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "启动了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "搞定了");
                countDownLatch.countDown();
            }

            ;
        }.start();
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "启动了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "搞定了");
                countDownLatch.countDown();
            }

            ;
        }.start();
        //注意加上这句 否则你这个CountDownLatch相当于没用 等待n个线程执行完毕 加入没有达到n个就会堵塞
        countDownLatch.await();
        System.out.println("main方法继续");
    }
}

执行结果是

CountDownLatch怎么使用?一个简单的Demo与使用注意事项_第1张图片

首先,主方法中的这句countDownLatch.await(); 很多人会漏掉,这句方法的底层代码是调用了Unsafe类的park()方法,也就是说调用了操作系统的线程挂起方法:

CountDownLatch怎么使用?一个简单的Demo与使用注意事项_第2张图片

当你把countDownLatch.await(); 注释后,你会观察到执行结果是:

CountDownLatch怎么使用?一个简单的Demo与使用注意事项_第3张图片

从执行结果可以得出,在子线程执行完之前主线程已经在继续执行,所以CountDowLatch并没有起效,这个就是普通的多线程执行结果,并没有出现主线程等待到n个子线程执行完毕才继续执行的结果 

 

第二个容易忽略的地方就是CountDownLatch的计数器数量,也就是代码中构造函数中的n的值,假如你的代码中子线程的总数并没有达到这个值或者没有执行countDownLatch.countDown();这条语句,那么你的主线程将一直等待,(原本他在等待n个子线程执行完毕,可是并没有n个子线程)

当把第二个线程注释掉:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main方法开始");
        int n = 2;
        final CountDownLatch countDownLatch = new CountDownLatch(n);
        Executor executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "启动了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "搞定了");
                countDownLatch.countDown();
            }

            ;
        }.start();
//        new Thread() {
//            @Override
//            public void run() {
//                System.out.println(Thread.currentThread().getName() + "启动了");
//                try {
//                    Thread.sleep(1000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                System.out.println(Thread.currentThread().getName() + "搞定了");
                countDownLatch.countDown();
//            }
//
//            ;
//        }.start();
        //注意加上这句 否则你这个CountDownLatch相当于没用 等待n个线程执行完毕 加入没有达到n个就会堵塞
        countDownLatch.await();
        System.out.println("main方法继续");
    }
}

CountDownLatch怎么使用?一个简单的Demo与使用注意事项_第4张图片

从执行结果可以得出,主线程一直在等待,System.out.println("main方法继续");并没有执行,说明程序并没有结束

 

第二种情况,我们只注释 countDownLatch.countDown();

public class Test {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main方法开始");
        int n = 2;
        final CountDownLatch countDownLatch = new CountDownLatch(n);
        Executor executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "启动了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "搞定了");
                countDownLatch.countDown();
            }

            ;
        }.start();
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "启动了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "搞定了");
//                countDownLatch.countDown();
            };
        }.start();
        //注意加上这句 否则你这个CountDownLatch相当于没用 等待n个线程执行完毕 加入没有达到n个就会堵塞
        countDownLatch.await();
        System.out.println("main方法继续");
    }
}

执行结果:

CountDownLatch怎么使用?一个简单的Demo与使用注意事项_第5张图片

执行结果跟上面一样,主线程一直在等待,System.out.println("main方法继续");并没有执行,说明程序并没有结束。

 

下次我将介绍一个与他相似的“兄弟”——CyclicBarrier

 

你可能感兴趣的:(java后端,java基础,CountDownLatch,并发,线程,多线程,线程计数器)