Java 多线程之CountDownLatch 计数器

CountDownLatch 计数器

CountDownLatch 是一个非常实用的多线程控制工具类,称之为“倒计时器”它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

   1 每一个线程执行完毕之后,都需要执行countDownLatch.countDown()方法,不然计数器就不会准确;

   2 只有所有的线程执行完毕之后,才会执行 countDownLatch.await()之后的代码;

CountDownLatch 阻塞的是主线程(也就是启动线程 会进行阻塞,必须等待所有的子线程执行完成才能执行)与 CountDownLatch 的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用 CountDownLatch.await() 方法。

这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。

   其他 N 个线程必须引用闭锁对象,因为他们需要通知 CountDownLatch 对象,他们已经完成了各自的任务。
   这种通知机制是通过 CountDownLatch.countDown() 方法来完成的;每调用一次这个方法,在构造函数中初始化的 count 值就减1。
   所以当 N 个线程都调用了这个方法,count 的值等于0,然后主线程就能通过 await() 方法,恢复执行自己的任务。

官方解释

CountDownLatch 是在 java1.5 被引入的,它存在于 java.util.concurrent 包下。CountDownLatch 这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

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

在这里插入图片描述

费话不多少上代码

启动七个线程模拟快递员收集七个快递


        //需要等待执行完毕的线程数量。
        CountDownLatch countDownLatch = new CountDownLatch(7);

        for (int i = 1; i <= 7; i++) {
            int index = i;
            new Thread(() -> {
                try {

                    System.out.println("第" + index + "个快递已收集到!");
                    //寻找时间
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                //计数减一   CountDownLatch.countDown() 方作为通知方法 每调用一次值减1 直到值为0表示执行完 才会去通知 awaut()方法 执行后面的代码
                countDownLatch.countDown();
 
            }).start();


        }

        //所有的线程执行完毕之后 彩会执行 await 之后的代码
        try {
            countDownLatch.await();
            System.out.println("   收集完成,快递员走了。   ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

结果

在这里插入图片描述

如果线程中不使用线程计数不是用计数器 CountDownLatch的时候

执行结果如下
快递还没收完,快递小哥已经走了,这不就出问题了嘛。
在这里插入图片描述
计数器 CountDownLatch 作用就是等待包裹一个个被快递小哥收集,收集完后,小哥才能走。

CountDownLatch 方法

void countDown() 计数方法减1 每个线
long getCount() 获取计数器的值
boolean await() 一直等待子线程计数完成 才执行之后代码
boolean await(long timeout, TimeUnit unit) 启动子线程等待计数阻塞时间,超过时间就不进行等待了。

使用场景

1 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
2 开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。
3 死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。

死锁检测


        Object o1 = new Object();
        Object o2 = new Object();

        Thread a = new Thread(() -> {
            countDownLatch.countDown();
            synchronized (o1) {

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                //获取o2 锁
                synchronized (o2) {

                }

            }

        });

        Thread a2 = new Thread(() -> {
            countDownLatch.countDown();
            synchronized (o2) {

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                //获取o2 锁
                synchronized (o1) {
                }

            }

        });


        a.start();
        a2.start();


        countDownLatch.await();

        System.out.println(" main  ");



执行结果

在这里插入图片描述
可以看到计数器执行完毕,但是JVM还未停止

jps 查看pid

在这里插入图片描述
可以看到我的类进程id

jstack 查看死锁信息

C:\Users\tizzy>jstack 26848
2020-04-28 15:39:54
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):

"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002e17000 nid=0x6048 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #14 prio=5 os_prio=0 tid=0x000000001a7e3000 nid=0x6eac waiting for monitor entry [0x000000001aecf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.bwjf.service.UserLcService.lambda$main$1(UserLcService.java:143)
        - waiting to lock <0x00000000d73d79f0> (a java.lang.Object)
        - locked <0x00000000d73d7a00> (a java.lang.Object)
        at cn.bwjf.service.UserLcService$$Lambda$2/910091170.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Thread-0" #13 prio=5 os_prio=0 tid=0x000000001a7c0800 nid=0x4f80 waiting for monitor entry [0x000000001adcf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.bwjf.service.UserLcService.lambda$main$0(UserLcService.java:124)
        - waiting to lock <0x00000000d73d7a00> (a java.lang.Object)
        - locked <0x00000000d73d79f0> (a java.lang.Object)
        at cn.bwjf.service.UserLcService$$Lambda$1/2028017635.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #12 daemon prio=9 os_prio=0 tid=0x0000000019a1c800 nid=0x4d0 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #11 daemon prio=9 os_prio=2 tid=0x0000000019998800 nid=0x7870 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #10 daemon prio=9 os_prio=2 tid=0x0000000019995000 nid=0x36c8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #9 daemon prio=9 os_prio=2 tid=0x0000000019971000 nid=0x5068 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Command Reader" #8 daemon prio=10 os_prio=0 tid=0x0000000019687800 nid=0x7d30 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #7 daemon prio=10 os_prio=0 tid=0x0000000019686000 nid=0x3928 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #6 daemon prio=10 os_prio=0 tid=0x000000001967a000 nid=0x7748 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000019676800 nid=0x76e4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001962b800 nid=0x48a4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000002f0c800 nid=0x579c in Object.wait() [0x00000000195be000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d6008ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000d6008ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002f0b000 nid=0x51d0 in Object.wait() [0x00000000194be000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d6006bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000d6006bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x0000000017c08800 nid=0x6e10 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002e2c800 nid=0x5af8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002e2e000 nid=0x24ec runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002e2f800 nid=0x7c48 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002e31000 nid=0x7d78 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002e34800 nid=0x79b0 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002e35800 nid=0x7dbc runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000019a1e000 nid=0x1434 waiting on condition

JNI global references: 4497


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001a722c48 (object 0x00000000d73d79f0, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001a723488 (object 0x00000000d73d7a00, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at cn.bwjf.service.UserLcService.lambda$main$1(UserLcService.java:143)
        - waiting to lock <0x00000000d73d79f0> (a java.lang.Object)
        - locked <0x00000000d73d7a00> (a java.lang.Object)
        at cn.bwjf.service.UserLcService$$Lambda$2/910091170.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0":
        at cn.bwjf.service.UserLcService.lambda$main$0(UserLcService.java:124)
        - waiting to lock <0x00000000d73d7a00> (a java.lang.Object)
        - locked <0x00000000d73d79f0> (a java.lang.Object)
        at cn.bwjf.service.UserLcService$$Lambda$1/2028017635.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.


C:\Users\tizzy>

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