有益的CountDownLatch和棘手的Java死鎖

您是否曾經使用過java.util.concurrent.CountDownLatch ?

這是在兩個或多個線程之間實現同步的非常方便的類,在該類中,一個或多個線程可以等待,直到在其他線程中執行的一組操作完成為止(請參閱javadoc和此文章 )。 CountDownLatch在適當的情況下可以節省您的時間,您必須了解此類。

與往常一樣,如果代碼不好,線程同步會引發死鎖。 由于并發用例可能非常復雜,因此開發人員必須非常小心。 在這里我不會描述復雜的并發問題,但是如果您不小心使用CountDownLatch ,可能會遇到一個簡單的問題。

假設您有2個線程(線程1和線程2)共享一個java.util.concurrent.ArrayBlockingQueue,并且您想使用CountDownLatch對其進行同步。 檢查以下簡單示例:

package gr.qiozas.simple.threads.countdownlatch;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;public class DeadlockCaseCDL {public static void main(String[] args) throws InterruptedException {CountDownLatch c = new CountDownLatch(1);ArrayBlockingQueue b = new ArrayBlockingQueue(1);new Thread(new T1(c, b)).start();new Thread(new T2(c, b)).start();}private static class T1 implements Runnable {private CountDownLatch c;private ArrayBlockingQueue b;private T1(CountDownLatch c, ArrayBlockingQueue b) {this.c = c; this.b = b;}public void run() {try {b.put(234);b.put(654);doWork(T1.class);c.countDown();doWork(T1.class);} catch (InterruptedException ex) {}}}private static class T2 implements Runnable {private CountDownLatch c;private ArrayBlockingQueue b;private T2(CountDownLatch c, ArrayBlockingQueue b) {this.c = c; this.b = b;}public void run() {try {doWork(T2.class);c.await();doWork(T2.class);System.out.println("I just dequeue "+b.take());System.out.println("I just dequeue "+b.take());} catch (InterruptedException ex) {}}}private static void doWork(Class classz) {System.out.println(classz.getName()+" do the work");}
}

您在上面看到的是,主線程創建了一個計數為1的CountDownLatch。 和一個容量為“ 1”的ArrayBlockingQueue 然后生成“ 2個線程”。 ArrayBlockingQueue將用于實際的“工作”(入隊和出隊),而CountDownLatch將用于同步線程(入隊必須在出隊前完成)。

線程1使2條消息入隊,線程2希望使它們出隊,但僅在線程1使兩條消息入隊之后。 此同步由CountDownLatch保證。 您認為此代碼可以嗎? 不,這不是造成死鎖的原因!

如果仔細看第10行,您將看到我初始化ArrayBlockingQueue的容量等于“ 1”。 但是線程1要排隊2條消息,然后釋放(CountDownLatch)的鎖,以便隨后被線程2占用。 容量“ 1? 隊列阻塞線程1,直到另一個線程將一個消息從隊列中出隊,然后再次嘗試使第二條消息入隊。 不幸的是,只有線程2使消息從隊列中出隊,但是由于線程1擁有CountDownLatch鎖,因此線程2無法使任何消息出隊,因此被阻塞。 因此,由于兩個線程都被阻塞(等待獲取不同的鎖),我們確實有一個死鎖 。 線程1等待ArrayBlockingQueue鎖定,而線程2等待CountDownLatch鎖定(您也可以在下面的相關線程轉儲中看到它)。

如果我們增加隊列的容量,那么此代碼將毫無問題地運行,但這不是本文的重點。 您需要了解的是,必須謹慎使用CountDownLatch,以避免此類危險情況。 您必須了解類的功能情況,向團隊的其他開發人員詳細說明該功能,編寫有用的Javadoc,并始終編寫在極端情況下(不僅對于開心路徑而言)都可靠的代碼。

您可能會幫助您的另一點是,現代JVM無法檢測到此死鎖。 嘗試一下。

如您所知,現代JVM(Hotspot和JRockit)都能夠檢測到簡單的死鎖,并在線程轉儲中報告它們。 請參閱從Hotspot JVM檢測到的簡單死鎖示例:

Found one Java-level deadlock:
=============================
"Thread-6":
waiting to lock monitor 0x00a891ec (object 0x06c616e0, a java.lang.String),
which is held by "Thread-9"
"Thread-9":
waiting to lock monitor 0x00a8950c (object 0x06c61708, a java.lang.String),
which is held by "Thread-6"

您可以嘗試DeadlockCaseCDL并獲得線程轉儲(在GNU / Linux上僅運行“ kill -3 ?jvm_pid?”)。 您將看到線程轉儲看起來很正常,JVM沒有檢測到死鎖,但是您處于死鎖狀態!!! 因此,請注意,JVM無法檢測到這種死鎖。

從我的本地執行中檢查以下線程轉儲示例:

Full thread dump Java HotSpot(TM) Server VM (17.1-b03 mixed mode):"DestroyJavaVM" prio=10 tid=0x0946e800 nid=0x5382 waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"Thread-1" prio=10 tid=0x094b1400 nid=0x5393 waiting on condition [0x7c79a000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for   (a java.util.concurrent.CountDownLatch$Sync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:969)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1281)at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:207)at gr.qiozas.simple.threads.countdownlatch.DeadlockCaseCDL$T2.run(DeadlockCaseCDL.java:50)at java.lang.Thread.run(Thread.java:662)"Thread-0" prio=10 tid=0x094afc00 nid=0x5392 waiting on condition [0x7c7eb000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for   (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:252)at gr.qiozas.simple.threads.countdownlatch.DeadlockCaseCDL$T1.run(DeadlockCaseCDL.java:29)at java.lang.Thread.run(Thread.java:662)"Low Memory Detector" daemon prio=10 tid=0x0947f800 nid=0x5390 runnable [0x00000000]java.lang.Thread.State: RUNNABLE"CompilerThread1" daemon prio=10 tid=0x7cfa9000 nid=0x538f waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"CompilerThread0" daemon prio=10 tid=0x7cfa7000 nid=0x538e waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"Signal Dispatcher" daemon prio=10 tid=0x7cfa5800 nid=0x538d waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"Finalizer" daemon prio=10 tid=0x7cf96000 nid=0x538c in Object.wait() [0x7cd15000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on  (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)- locked  (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)"Reference Handler" daemon prio=10 tid=0x7cf94800 nid=0x538b in Object.wait() [0x7cd66000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on  (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:485)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)- locked  (a java.lang.ref.Reference$Lock)"VM Thread" prio=10 tid=0x7cf92000 nid=0x538a runnable"GC task thread#0 (ParallelGC)" prio=10 tid=0x09475c00 nid=0x5383 runnable"GC task thread#1 (ParallelGC)" prio=10 tid=0x09477000 nid=0x5384 runnable"GC task thread#2 (ParallelGC)" prio=10 tid=0x09478800 nid=0x5385 runnable"GC task thread#3 (ParallelGC)" prio=10 tid=0x0947a000 nid=0x5387 runnable"VM Periodic Task Thread" prio=10 tid=0x09489800 nid=0x5391 waiting on conditionJNI global references: 854HeapPSYoungGen      total 14976K, used 1029K [0xa2dd0000, 0xa3e80000, 0xb39d0000)eden space 12864K, 8% used [0xa2dd0000,0xa2ed1530,0xa3a60000)from space 2112K, 0% used [0xa3c70000,0xa3c70000,0xa3e80000)to   space 2112K, 0% used [0xa3a60000,0xa3a60000,0xa3c70000)PSOldGen        total 34304K, used 0K [0x815d0000, 0x83750000, 0xa2dd0000)object space 34304K, 0% used [0x815d0000,0x815d0000,0x83750000)PSPermGen       total 16384K, used 1739K [0x7d5d0000, 0x7e5d0000, 0x815d0000)object space 16384K, 10% used [0x7d5d0000,0x7d782e90,0x7e5d0000)

參考: 有益的CountDownLatch和我們的JCG合作伙伴 Adrianos Dadis 遇到的棘手的Java僵局 ,位于“ Java,集成和源代碼的美德”博客上 。

相關文章 :
  • Java并發教程– CountDownLatch
  • 并發優化–減少鎖粒度
  • Java內存模型–快速概述和注意事項
  • Java并發教程–線程池
  • Java并發教程–信號量
  • Java教程和Android教程列表

翻譯自: https://www.javacodegeeks.com/2011/11/beneficial-countdownlatch-and-tricky.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374213.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374213.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374213.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法—回溯法橋本分數式

/* 將1-9九個數不重復地賦給不同的9個元素 ,實現形如a/bcd/eff/hi 的形式:例:1/265/784/39 1/325/967/84 (注意:1/265/784/39 和5/781/264/39 只能算一種解)求滿足條件的解共有多少個? */ #in…

codeforces 703B

題意:有n座城市,其中k座是省會城市,每個城市有對應的點權,城市1-2-3-...-n-1有一條路相連,省會城市與其他所有的城市相連,且每兩個城市間最多有一條路,每條路的邊權為路連接的兩座城市的點權乘積…

go 基準測試 找不到函數_基于Golang做測試

本文在實習期間完成并完善,無任何公司機密,僅做語言交流學習之用。持續更新。1.Golang的單元測試Go語言提供了豐富的單測功能。在Go中,我們通常認為函數是最小的可執行單元。本例中使用兩個簡單的函數:IsOdd和IsPalindrome來進行G…

九齊NY8B072A單片機使用筆記(三)模擬串口RX

因為這款單片機沒有硬件串口,所以需要我們自己做軟件模擬串口。 用PA3作為RX,因為PA3可以作為外部輸入中斷EXTI1。 本人首先用輪詢的方式查PA3是否從高電平跳變到低電平(起始信號),但是因為還有別的業務邏輯&#xf…

Java RESTful API集成測試

這篇文章將重點介紹為RESTful API(帶有JSON有效負載)編寫Java集成測試的基本原理和機制。 目的是對技術進行介紹,并為基本正確性編寫一些測試。 這些示例將使用最新版本的GitHub REST API。 對于內部應用程序,這種測試通常將在持…

java警惕自增的陷阱

public class proposal{public static void main(String[] args) {int count0;for(int i0;i<10;i){countcount;}System.out.println(count);}}結果輸出&#xff1a;0/*步驟一&#xff1a;JMV吧count值&#xff08;其值是0&#xff09;拷貝到臨時變量區&#xff1b;步驟二:co…

[LindCode] Binary Tree Postorder Traversal

Binary Tree Postorder Traversal Given a binary tree, return the postorder traversal of its nodes values. Example Given binary tree {1,#,2,3}, 1\2/3return [3,2,1]. Challenge Can you do it without recursion? SOLUTION 1: recursion&#xff1a; 分治法解決之&am…

九齊NY8B072A單片機使用筆記(一)TIMER0定時器

先上代碼 //8bit count up , max 0xFF void Ny8b072a_Timer0_Init(void) {PCON1 C_TMR0_Dis; // Disable Timer0//1 * (255 - 5) 250usTMR0 5; // Load 0x00 to TMR0 (Initial Timer0 register)//16M 2T Div8 1usT0MD C_PS0_TMR0 | C_PS0_Div8 ; // Prescaler0 is assign…

python菜鳥教程split_Python split()方法

網頁地址解析&#xff1a; #codingutf-8 str"http://www.runoob.com/python/att-string-split.html" print("0:%s"%str.split("/")[-1]) print("1:%s"%str.split("/")[-2]) print("2:%s"%str.split("/"…

金山毒霸垃圾清理

金山毒霸-垃圾清理-單文件封裝,清潔潔癖的愛好&#xff01; 實話&#xff0c;金山的軟件確實不錯。展望金山可以在軟件行業&#xff0c;做出讓世界都使用的。為國人扛起一片天 下載地址&#xff1a; http://pan.baidu.com/s/1dFa7GdV轉載于:https://www.cnblogs.com/xiaochina/…

并發優化–減少鎖粒度

在高負載多線程應用程序中&#xff0c;性能非常重要。 開發人員必須意識到并發問題才能獲得更好的性能。 當我們需要并發時&#xff0c;我們通常擁有必須由兩個或更多線程共享的資源。 在這種情況下&#xff0c;我們有一個競爭條件 &#xff0c;其中只有一個線程&#xff08;在…

Java1.5增加了新特性:可變參數

/*Java 可變參數Java1.5增加了新特性&#xff1a;可變參數&#xff1a;適用于參數個數不確定&#xff0c;類型確定的情況&#xff0c;java把可變參數當做數組處理。注意&#xff1a;可變參數必須位于最后一項。當可變參數個數多余一個時&#xff0c;必將有一個不是最后一項&…

C語言代碼規范(十)花里胡哨代碼鑒賞

一、宏定義篇 1、作者的目的是防止GPIO口賦值超過1。但是有明顯自覺高人一等&#xff0c;瞧不起讀者的感覺。 uint8_t not_func(uint8_t sw) {return (sw?1:0); }#define LED1(sw) PA12not_func(sw)修改建議&#xff1a; #define LED1 PA12 #define LED_ON 0 #de…

python-break循環中斷

Python break語句&#xff0c;就像在C語言中&#xff0c;打破了最小封閉for或while循環。 break語句用來終止循環語句&#xff0c;即循環條件沒有False條件或者序列還沒被完全遞歸完&#xff0c;也會停止執行循環語句。 break語句用在while和for循環中。 如果您使用嵌套循環&am…

正則表達式驗證六位數以上數字,符號,字母任意兩種混合的密碼驗證策略

^(?![0-9]$)(?![a-zA-Z]$)(?!([^(0-9a-zA-Z)]|[\(\)])$)([^(0-9a-zA-Z)]|[\(\)]|[a-zA-Z]|[0-9]){6,}$這個正則如果是單獨的數字&#xff0c;字符和符號&#xff0c;是不能通過的&#xff0c;少于6位也不行&#xff0c;希望大家可以繼續驗證正確性吧轉載于:https://www.cnbl…

python post請求實例_Python使用requests發送POST請求實例代碼

本文研究的主要是Python使用requests發送POST請求的相關內容&#xff0c;具體介紹如下。 一個http請求包括三個部分&#xff0c;為別為請求行&#xff0c;請求報頭&#xff0c;消息主體&#xff0c;類似以下這樣&#xff1a; 請求行 請求報頭 消息主體 HTTP協議規定post提交的數…

Java Micro-Benchmarking:如何編寫正確的基準

幾個月前&#xff0c;我寫了一篇文章比較循環的短索引的性能 。 我問自己關于使用短褲作為循環迭代次數很少的循環的性能。 在Java語言中&#xff0c;所有對整數的操作都是int進行的。 因此&#xff0c;如果我們使用short作為循環索引&#xff0c;則在每次迭代時都將進行類型轉…

新唐M031學習筆記(一)定時器基礎計數應用

先上代碼 void Hw_Timer0_Init(void) {//20:100ms 200:10ms 2000:1ms 20000:100us 200000:10us TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 200000);/* Update prescale to set proper resolution. */TIMER_SET_PRESCALE_VALUE(TIMER0, 1); /* Enable Timer0 interrupt */TI…

java三元操作符注意

/* 三元操作符的類型務必一致 */ public class proposal_3 {public static void main(String[] args) {int i80;String sString.valueOf(i<90?90:100);String s1String.valueOf(i<90?90:100.0);if(s.equals(s1))System.out.println("s和s1相等&#xff01;"…

緩解口臭可以喝一種水

河南中醫學院第一附屬醫院耳鼻喉科主任醫師梅祥勝點評&#xff1a;通常情況下&#xff0c;口臭跟脾胃濕熱有關。中醫講&#xff1a;“胃主受納&#xff0c;脾主運化&#xff1b;胃氣主降&#xff0c;使飲食物及 其糟粕得以下行&#xff0c;脾氣主升&#xff0c;則飲食物之精華得…