❶ 求解java多線程的死鎖
你這是同步鎖,鎖的是A對象。有線程跟你一樣用 A對象當鎖的時候 ,只會有一條線程 來執行 B。其他線程都得等待。
1.A區域究竟什麼對象可以作為鎖?
對象,類對象。類對象 全局只有一個 比如 A.class ,當有人用到 這個類對象的時候 就會將其鎖住。不讓其他線程進入。
2.是不是我在一個線程中將A這個對象作為鎖,在另一個線程中對A這個對象進行操作,就會發生死鎖?
死鎖的根本原因1)是多個線程涉及到多個鎖,這些鎖存在著交叉,所以可能會導致了一個鎖依賴的閉環;2)默認的鎖申請操作是阻塞的。所以要避免死鎖,就要在一遇到多個對象鎖交叉的情況,就要仔細審查這幾個對象的類中的所有方法,是否存在著導致鎖依賴的環路的可能性。要採取各種方法來杜絕這種可能性。
你這樣 鎖不到的。舉個例子 死鎖 就是 x線程 鎖住了 A對象 然後 調用B對象的方法,y線程 鎖住了B對象調用A對象的方法,兩邊 都在互相嘗試獲取對方的鎖,但是拿不到。因為 x鎖住了A對象。y鎖住了B對象。他們互相拿不到 就叫死鎖。這只是個例子還有很多。
3不是說任何對象都可以作為一把鎖嗎?那麼每一個鎖我使用一個獨立的成員對象作為鎖,不就是可以很容易避開死鎖嗎?為什麼說死鎖很容易發生?
你每個鎖用一個獨立的成員對象作為鎖,沒問題,只要沒有存在交叉。上面那個例子一樣。
避免死鎖是一件困難的事,遵循以下原則有助於規避死鎖:
1、只在必要的最短時間內持有鎖,考慮使用同步語句塊代替整個同步方法;
2、盡量編寫不在同一時刻需要持有多個鎖的代碼,如果不可避免,則確保線程持有第二個鎖的時間盡量短暫;
3、創建和使用一個大鎖來代替若干小鎖,並把這個鎖用於互斥,而不是用作單個對象的對象級別鎖;
❷ 請教JAVA多線程中死鎖的問題
1.Java中導致死鎖的原因Java中死鎖最簡單的情況是,一個線程T1持有鎖L1並且申請獲得鎖L2,而另一個線程T2持有鎖L2並且申請獲得鎖L1,因為默認的鎖申請操作都是阻塞的,所以線程T1和T2永遠被阻塞了。導致了死鎖。這是最容易理解也是最簡單的死
❸ 如何避免Java多線程中的死鎖
死鎖產生的必要條件
產生死鎖必須同時滿足以下四個條件,只要其中任一條件不成立,死鎖就不會發生。
互斥條件:進程要求對所分配的資源(如列印機)進行排他性控制,即在一段時間內某 資源僅為一個進程所佔有。此時若有其他進程請求該資源,則請求進程只能等待。
不剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來釋放(只能是主動釋放)。
請求和保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他進程佔有,此時請求進程被阻塞,但對自己已獲得的資源保持不放。
循環等待條件:存在一種進程資源的循環等待鏈,鏈中每一個進程已獲得的資源同時被 鏈中下一個進程所請求。即存在一個處於等待狀態的進程集合{Pl, P2, ..., pn},其中Pi等 待的資源被P(i+1)佔有(i=0, 1, ..., n-1),Pn等待的資源被P0佔有
❹ JAVA多線程死鎖問題
1. Java中導致死鎖的原因
Java中死鎖最簡單的情況是,一個線程T1持有鎖L1並且申請獲得鎖L2,而另一個線程T2持有鎖L2並且申請獲得鎖L1,因為默認的鎖申請操作都是阻塞的,所以線程T1和T2永遠被阻塞了。導致了死鎖。這是最容易理解也是最簡單的死鎖的形式。但是實際環境中的死鎖往往比這個復雜的多。可能會有多個線程形成了一個死鎖的環路,比如:線程T1持有鎖L1並且申請獲得鎖L2,而線程T2持有鎖L2並且申請獲得鎖L3,而線程T3持有鎖L3並且申請獲得鎖L1,這樣導致了一個鎖依賴的環路:T1依賴T2的鎖L2,T2依賴T3的鎖L3,而T3依賴T1的鎖L1。從而導致了死鎖。
從這兩個例子,我們可以得出結論,產生死鎖可能性的最根本原因是:線程在獲得一個鎖L1的情況下再去申請另外一個鎖L2,也就是鎖L1想要包含了鎖L2,也就是說在獲得了鎖L1,並且沒有釋放鎖L1的情況下,又去申請獲得鎖L2,這個是產生死鎖的最根本原因。另一個原因是默認的鎖申請操作是阻塞的。
2. Java中如何避免死鎖
既然我們知道了產生死鎖可能性的原因,那麼就可以在編碼時進行規避。Java是面向對象的編程語言,程序的最小單元是對象,對象封裝了數據和操作,所以Java中的鎖一般也是以對象為單位的,對象的內置鎖保護對象中的數據的並發訪問。所以如果我們能夠避免在對象的同步方法中調用其它對象的同步方法,那麼就可以避免死鎖產生的可能性。如下所示的代碼,就存在死鎖的可能性:
public class ClassB {
private String address;
// ...
public synchronized void method1(){
// do something
}
// ... ...
}
public class ClassA {
private int id;
private String name;
private ClassB b;
// ...
public synchronized void m1(){
// do something
b.method1();
}
// ... ...
}
上面的ClassA.m1()方法,在對象的同步方法中又調用了ClassB的同步方法method1(),所以存在死鎖發生的可能性。我們可以修改如下,避免死鎖:
public class ClassA {
private int id;
private String name;
private ClassB b;
// ...
public void m2(){
synchronized(this){
// do something
}
b.method1();
}
// ... ...
}
這樣的話減小了鎖定的范圍,兩個鎖的申請就沒有發生交叉,避免了死鎖的可能性,這是最理性的情況,因為鎖沒有發生交叉。但是有時是不允許我們這樣做的。此時,如果只有ClassA中只有一個m1這樣的方法,需要同時獲得兩個對象上的鎖,並且不會將實例屬性 b 溢出(return b;),而是將實例屬性 b 封閉在對象中,那麼也不會發生死鎖。因為無法形成死鎖的閉環。但是如果ClassA中有多個方法需要同時獲得兩個對象上的鎖,那麼這些方法就必須以相同的順序獲得鎖。
比如銀行轉賬的場景下,我們必須同時獲得兩個賬戶上的鎖,才能進行操作,兩個鎖的申請必須發生交叉。這時我們也可以打破死鎖的那個閉環,在涉及到要同時申請兩個鎖的方法中,總是以相同的順序來申請鎖,比如總是先申請 id 大的賬戶上的鎖 ,然後再申請 id 小的賬戶上的鎖,這樣就無法形成導致死鎖的那個閉環。
public class Account {
private int id; // 主鍵
private String name;
private double balance;
public void transfer(Account from, Account to, double money){
if(from.getId() > to.getId()){
synchronized(from){
synchronized(to){
// transfer
}
}
}else{
synchronized(to){
synchronized(from){
// transfer
}
}
}
}
public int getId() {
return id;
}
}
這樣的話,即使發生了兩個賬戶比如 id=1的和id=100的兩個賬戶相互轉賬,因為不管是哪個線程先獲得了id=100上的鎖,另外一個線程都不會去獲得id=1上的鎖(因為他沒有獲得id=100上的鎖),只能是哪個線程先獲得id=100上的鎖,哪個線程就先進行轉賬。這里除了使用id之外,如果沒有類似id這樣的屬性可以比較,那麼也可以使用對象的hashCode()的值來進行比較。
上面我們說到,死鎖的另一個原因是默認的鎖申請操作是阻塞的,所以如果我們不使用默認阻塞的鎖,也是可以避免死鎖的。我們可以使用ReentrantLock.tryLock()方法,在一個循環中,如果tryLock()返回失敗,那麼就釋放以及獲得的鎖,並睡眠一小段時間。這樣就打破了死鎖的閉環。
比如:線程T1持有鎖L1並且申請獲得鎖L2,而線程T2持有鎖L2並且申請獲得鎖L3,而線程T3持有鎖L3並且申請獲得鎖L1
此時如果T3申請鎖L1失敗,那麼T3釋放鎖L3,並進行睡眠,那麼T2就可以獲得L3了,然後T2執行完之後釋放L2, L3,所以T1也可以獲得L2了執行完然後釋放鎖L1, L2,然後T3睡眠醒來,也可以獲得L1, L3了。打破了死鎖的閉環。
這些情況,都還是比較好處理的,因為它們都是相關的,我們很容易意識到這里有發生死鎖的可能性,從而可以加以防備。很多情況的場景都不會很明顯的讓我們察覺到會存在發生死鎖的可能性。所以我們還是要注意:
一旦我們在一個同步方法中,或者說在一個鎖的保護的范圍中,調用了其它對象的方法時,就要十而分的小心:
1)如果其它對象的這個方法會消耗比較長的時間,那麼就會導致鎖被我們持有了很長的時間;
2)如果其它對象的這個方法是一個同步方法,那麼就要注意避免發生死鎖的可能性了;
最好是能夠避免在一個同步方法中調用其它對象的延時方法和同步方法。如果不能避免,就要採取上面說到的編碼技巧,打破死鎖的閉環,防止死鎖的發生。同時我們還可以盡量使用「不可變對象」來避免鎖的使用,在某些情況下還可以避免對象的共享,比如 new 一個新的對象代替共享的對象,因為鎖一般是對象上的,對象不相同了,也就可以避免死鎖,另外盡量避免使用靜態同步方法,因為靜態同步相當於全局鎖。還有一些封閉技術可以使用:比如堆棧封閉,線程封閉,ThreadLocal,這些技術可以減少對象的共享,也就減少了死鎖的可能性。
❺ java多線程中的死鎖,活鎖,飢餓,無鎖都是什麼鬼
死鎖發生在當一些進程請求其它進程佔有的資源而被阻塞時。
另外一方面,活鎖不會被阻塞,而是不停檢測一個永遠不可能為真的條件。除去進程本身持有的資源外,活鎖狀態的進程會持續耗費寶貴的CPU時間。
最後,進程會處於飢餓狀態是因為持續地有其它優先順序更高的進程請求相同的資源。不像死鎖或者活鎖,飢餓能夠被解開。例如,當其它高優先順序的進程都終止時並且沒有更高優先順序的進程到達。
❻ JAVA中如何去避免多線程產生的死鎖
Java線程死鎖需要如何解決,這個問題一直在我們不斷的使用中需要只有不斷的關鍵。不幸的是,使用上鎖會帶來其他問題。讓我們來看一些常見問題以及相應的解決方法: Java線程死鎖 Java線程死鎖是一個經典的多線程問題,因為不同的線程都在等待那些根本不可能被釋放的鎖,從而導致所有的工作都無法完成。假設有兩個線程,分別代表兩個飢餓的人,他們必須共享刀叉並輪流吃飯。他們都需要獲得兩個鎖:共享刀和共享叉的鎖。 假如線程 「A」獲得了刀,而線程「B」獲得了叉。線程「A」就會進入阻塞狀態來等待獲得叉,而線程「B」則阻塞來等待「A」所擁有的刀。這只是人為設計的例子,但盡管在運行時很難探測到,這類情況卻時常發生。雖然要探測或推敲各種情況是非常困難的,但只要按照下面幾條規則去設計系統,就能夠避免Java線程死鎖問題: 讓所有的線程按照同樣的順序獲得一組鎖。這種方法消除了 X 和 Y 的擁有者分別等待對方的資源的問題。 將多個鎖組成一組並放到同一個鎖下。前面Java線程死鎖的例子中,可以創建一個銀器對象的鎖。於是在獲得刀或叉之前都必須獲得這個銀器的鎖。 將那些不會阻塞的可獲得資源用變數標志出來。當某個線程獲得銀器對象的鎖時,就可以通過檢查變數來判斷是否整個銀器集合中的對象鎖都可獲得。如果是,它就可以獲得相關的鎖,否則,就要釋放掉銀器這個鎖並稍後再嘗試。 最重要的是,在編寫代碼前認真仔細地設計整個系統。多線程是困難的,在開始編程之前詳細設計系統能夠幫助你避免難以發現Java線程死鎖的問題。 Volatile 變數,volatile 關鍵字是 Java 語言為優化編譯器設計的。以下面的代碼為例: 1.class VolatileTest {
2.public void foo() {
3.boolean flag = false;
4.if(flag) {
5.//this could happen
6.}
7.}
8.} 一個優化的編譯器可能會判斷出if部分的語句永遠不會被執行,就根本不會編譯這部分的代碼。如果這個類被多線程訪問, flag被前面某個線程設置之後,在它被if語句測試之前,可以被其他線程重新設置。用volatile關鍵字來聲明變數,就可以告訴編譯器在編譯的時候,不需要通過預測變數值來優化這部分的代碼。 無法訪問的Java線程死鎖有時候雖然獲取對象鎖沒有問題,線程依然有可能進入阻塞狀態。在 Java 編程中IO就是這類問題最好的例子。當線程因為對象內的IO調用而阻塞時,此對象應當仍能被其他線程訪問。該對象通常有責任取消這個阻塞的IO操作。造成阻塞調用的線程常常會令同步任務失敗。如果該對象的其他方法也是同步的,當線程被阻塞時,此對象也就相當於被冷凍住了。 其他的線程由於不能獲得對象的Java線程死鎖,就不能給此對象發消息(例如,取消 IO 操作)。必須確保不在同步代碼中包含那些阻塞調用,或確認在一個用同步阻塞代碼的對象中存在非同步方法。盡管這種方法需要花費一些注意力來保證結果代碼安全運行,但它允許在擁有對象的線程發生阻塞後,該對象仍能夠響應其他線程。 編輯推薦: 1. Java多線程優化之偏向鎖原理分析 2. Java多線程實現非同步調用的方法 3. 使用Java多線程機制實現下載的方法介紹
❼ Java 多線程中 什麼是死鎖有什麼作用
所謂死鎖:
是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
由於資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而無法繼續運行,這就產生了一種特殊現象死鎖。
❽ 什麼是java多線程中的死鎖
一個進程在等待一個不可能發生的事,即進程死鎖,如果一個或多個進程產生死鎖,就會造成系統死鎖。
3、死鎖發生的必要條件
(1)、互斥條件:一個資源每次只能被一個進程使用,在操作系統中這是真實存在的情況。
(2)、保護和等待條件:有一個進程已獲得了一些資源,但因請求其他資源被阻塞時,對已獲得的資源保持不放。
(3)、不剝奪條件:有些系統資源是不可剝奪的,當某個進程已獲得這種資源後,系統不能強行收回,只能由進程使用完時自己釋放。
(4)、環路等待條件:若干個進程形成環行鏈,每個都佔用對方要申請的下一個資源。
❾ java多線程死鎖問題
public class Test520 {//測試類
public static void main(String[] args) {
Test1 t1=new Test1();//構造線程1
Test2 t2=new Test2();//構造線程2
t1.start();//啟動線程1
t2.start();//啟動線程2
}
}
class Test1 extends Thread{//線程類1
public void run() {//線程類1的run方法
synchronized (A.class) {//線程類1獲取A類的鎖
new A().a();//構建A類調用a方法,線程可以執行到這里
synchronized (B.class) {//線程1請求獲取B類的鎖,看後面的代碼我們知道B類的鎖在線程2中,形成死鎖
new B().b();//構造B類,調用b方法,這語句無法執行,因線程1始終無法獲得已被線程2獲得的B類鎖
}
}
}
}
class Test2 extends Thread{//線程類2
public void run() {//線程類2的run方法
synchronized (B.class) {//線程2獲取了B類的鎖,因此線程1無法在調用a方法後獲取B類鎖執行後面的語句
new A().a();//構造A類對象調用a方法,此語句可以執行
synchronized (A.class) {//線程2請求A類鎖,A類鎖此時被線程1持有
new B().b();//如果線程2能夠獲取A類鎖,就能執行這一步,測試知道,無法執行到這句
}
}
}
}
class A{//測試類
public void a() {
System.out.println("a");
}
}
class B{//測試類
public void b() {
System.out.println("b");
}
}
❿ 怎麼處理JAVA多線程死鎖問題
有兩種實現方法,分別是繼承Thread類與實現Runnable
介面
用synchronized關鍵字修飾同步方法
反對使用stop(),是因為它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處於一種不連貫狀態,那麼
其他線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用
suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定
的資源,除非被"掛起"的線程恢復運行。對任何線程來說,如果它們想恢復目標線程,同時又試圖使用任何一個
鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標志,指出線程應該
活動還是掛起。若標志指出線程應該掛起,便用wait()命其進入等待狀態。若標志指出線程應當恢復,則用一個
notify()重新啟動線程。