摘要:上一章介紹過關鍵字,使用它可以給程序互斥部分加上一把鎖從而達到同步的效果,但錯誤的用法會導致多個線程同時被阻塞死鎖死鎖多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由于線程被無限期地阻塞,因此程序不可能正常終止。
死鎖上一章介紹過synchronized關鍵字,使用它可以給程序互斥部分加上一把鎖從而達到同步的效果,但錯誤的用法會導致多個線程同時被阻塞....
死鎖: 多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由于線程被無限期地阻塞,因此程序不可能正常終止。
JAVA 中死鎖產生的四個必要條件
互斥使用,當資源被一個線程使用(占有)時,別的線程不能使用
不可搶占,資源請求者不能強制從資源占有者手中奪取資源,資源只能由資源占有者主動釋放。
請求和保持,當資源請求在請求其他的資源的同時保持對原有資源的占有。
循環等待,存在一個等待隊列:P1占有P2的資源,P2占有P3的資源,P3占有P1的資源。這樣就形成了一個等待環路。
經典案例先來看一段經典的死鎖代碼,兩個方法中分別獲取了class literals方式的鎖
void method1() { while (true) { synchronized (Integer.class) { System.out.println("獲得 Integer.class 對象鎖"); synchronized (String.class) { System.out.println("獲得 String.class 對象鎖"); } } } } void method2() { while (true) { synchronized (String.class) { System.out.println("獲得 String.class 對象鎖"); synchronized (Integer.class) { System.out.println("獲得 Integer.class 對象鎖"); } } } } public static void main(String[] args) { DeadLockFixed fixed = new DeadLockFixed(); new Thread(fixed::method1, "method1").start(); new Thread(fixed::method2, "method2").start(); } ////////////////////////日志//////////////////////// //獲得 Integer.class 對象鎖 //獲得 String.class 對象鎖 //獲得 Integer.class 對象鎖 //獲得 String.class 對象鎖 //獲得 Integer.class 對象鎖 //獲得 String.class 對象鎖 //獲得 Integer.class 對象鎖 //獲得 String.class 對象鎖 //獲得 Integer.class 對象鎖 //獲得 String.class 對象鎖 ////////////////////////日志////////////////////////
分析: method1 和 method2 都會去獲取Integer和String兩把鎖,但是獲取鎖的順序不一致,在并行情況,當兩個方法未及時釋放自己的鎖,又同時去獲取另外一把鎖這時候就會出現死鎖情況
舉個例子: 吃飯需要碗和筷子,小明吃飯的時候先拿筷子在拿碗,小紅相反,再一次拿取餐具的時候,小明先拿到筷子,正準備拿碗的時候被小紅搶先了一步拿碗,這時候都要吃飯但誰也不讓誰,結果就是都餓死(死鎖)
分析手段命令: JDK本身提供了很多方便的JVM性能調優監控工具,除了集成式的VisualVM和jConsole外,還有jps、jstack、jmap、jhat、jstat等小巧的命令工具,本章主要使用到jps與jstack做分析
jps: 過濾出Java本身的進程以及運行的引導類,就是引導main方法所在的類。
-q 僅輸出VM標識符,不包括class name,jar name,arguments in main method -m 輸出main method的參數 -l 輸出完全的包名,應用主類名,jar的完全路徑名 -v 輸出jvm參數 -V 輸出通過flag文件傳遞到JVM中的參數(.hotspotrc文件或-XX:Flags=所指定的文件 -Joption 傳遞參數到vm,例如:-J-Xms48m
jstack: 主要用來查看某個Java進程內的線程堆棧信息
-l long listings,會打印出額外的鎖信息,在發生死鎖時可以用jstack -l pid來觀察鎖持有情況 -m mixed mode,不僅會輸出Java堆棧信息,還會輸出C/C++堆棧信息(比如Native方法)
打開dos界面,輸入jps
找到當前main的pid,使用jstack pid命令
我們可以發現method1鎖住了<0x000000076ae03e68>等待<0x000000076ae008d8>,method2恰恰相反,這個時候死鎖就發生了...
日志滑動到后面,可以發現有一個Found one Java-level deadlock,然后也告訴了我們代碼在多少行,在知道問題代碼所在地后,解決起來就簡單了(改變鎖的順序,完美解決問題...)
補充兩句上面講了什么是死鎖以及定位問題方式,其實JDK中,已經內置了很多鎖相關的類都在java.util.concurrent.locks包下,使用簡單且可以有效的的避免死鎖問題,或者使用java.util.concurrent.Semaphore(信號量)機制,篇幅過長會導致閱讀興趣下降,所以信號量處理鎖的方式在GIT中,有興趣可以從GIT地址獲取
- 說點什么全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter6
個人QQ:1837307557
battcn開源群(適合新手):391619659
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67628.html
摘要:如果有其它線程調用了相同對象的方法,那么處于該對象的等待池中的線程就會全部進入該對象的鎖池中,從新爭奪鎖的擁有權。 wait,notify 和 notifyAll,這些在多線程中被經常用到的保留關鍵字,在實際開發的時候很多時候卻并沒有被大家重視,而本文則是對這些關鍵字的使用進行描述。 存在即合理 在java中,每個對象都有兩個池,鎖池(monitor)和等待池(waitset),每個...
摘要:每個對象只有一個鎖與之相關聯。實現同步則是以系統開銷作為代價,甚至可能造成死鎖,所以盡量避免濫用。這種機制確保了同一時刻該類實例,所有聲明為的函數中只有一個方法處于可執行狀態,從而有效避免了類成員變量訪問沖突。 synchronized是JAVA語言的一個關鍵字,使用 synchronized 來修飾方法或代碼塊的時候,能夠保證多個線程中最多只有一個線程執行該段代碼 ... 概述 ...
摘要:在之前,不能為線程單獨設置或指定一個默認的,為了設置,需要繼承并覆寫方法。幸運的是后線程提供了一個方法,用來捕獲并處理因線程中拋出的未知異常,以避免程序終止。 在單線程的開發過程中,通常采用try-catch的方式進行異常捕獲,但是這種方式在多線程環境中會顯得無能為力,而且還有可能導致一些問題的出現,比如發生異常的時候不能及時回收系統資源,或者無法及時關閉當前的連接... 概述 Ja...
摘要:在退出時執行必要的挽救措施。在這種情況下,一旦被提供,等待一個進程終止指定的時間。如果進程在該時間限制內沒有終止,則通過發出或中的對等方強制終止進程。所以有可能這是在中途執行時發生的。 shutdownHook是一種特殊的結構,它允許開發人員插入JVM關閉時執行的一段代碼。這種情況在我們需要做特殊清理操作的情況下很有用 用途 在Jboss,Jetty等容器中都可以看到shutdown...
摘要:我的是忙碌的一年,從年初備戰實習春招,年三十都在死磕源碼,三月份經歷了阿里五次面試,四月順利收到實習。因為我心理很清楚,我的目標是阿里。所以在收到阿里之后的那晚,我重新規劃了接下來的學習計劃,將我的短期目標更新成拿下阿里轉正。 我的2017是忙碌的一年,從年初備戰實習春招,年三十都在死磕JDK源碼,三月份經歷了阿里五次面試,四月順利收到實習offer。然后五月懷著忐忑的心情開始了螞蟻金...
閱讀 902·2021-11-22 13:53
閱讀 2532·2021-10-15 09:40
閱讀 1000·2021-10-14 09:42
閱讀 3474·2021-09-22 15:59
閱讀 887·2021-09-02 09:47
閱讀 2365·2019-08-30 15:54
閱讀 1437·2019-08-29 17:14
閱讀 398·2019-08-29 15:15