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>

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