⑴ java中 死鎖是同步造成的還是非同步造成的
死鎖是這要發生的。
A,B,C 三個線程都拿著一個資源同時請求另一個由其它人佔用的資源,多個參與者的情況也是類似的,反正就是像我們平時說的一手交錢,一手交貨,但雙方都不放手的過程。
因此只有至少2個參與方同時進行時才會死鎖,而同步是一個先做,另一個再接著做,那就不會死鎖,因為 A 沒完成時,B 還沒開始運行,所以 B 不可能搶了 A 需要的資源。
⑵ java重寫父類的同步方法會死鎖嗎
死鎖是多線程相互依賴的時候才會出現的問題。重寫父類同步方式並不會出現死鎖,只有多線程訪問的時候,程序邏輯寫錯,就會出現死鎖。
⑶ 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中的死鎖
死鎖是這樣一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由於線程被無限期地阻塞,因此程序不可能正常終止。
導致死鎖的根源在於不適當地運用「synchronized」關鍵詞來管理線程對特定對象的訪問。「synchronized」關鍵詞的作用是,確保在某個時刻只有一個線程被允許執行特定的代碼塊,因此,被允許執行的線程首先必須擁有對變數或對象的排他性的訪問權。當線程訪問對象時,線程會給對象加鎖,而這個鎖導致其它也想訪問同一對象的線程被阻塞,直至第一個線程釋放它加在對象上的鎖。
由於這個原因,在使用「synchronized」關鍵詞時,很容易出現兩個線程互相等待對方做出某個動作的情形。代碼一是一個導致死鎖的簡單例子。
//代碼一
class Deadlocker {
int field_1;
private Object lock_1 = new int[1];
int field_2;
private Object lock_2 = new int[1];
public void method1(int value) {
「synchronized」 (lock_1) {
「synchronized」 (lock_2) {
field_1 = 0; field_2 = 0;
}
}
}
public void method2(int value) {
「synchronized」 (lock_2) {
「synchronized」 (lock_1) {
field_1 = 0; field_2 = 0;
}
}
}
}
參考代碼一,考慮下面的過程:
◆ 一個線程(ThreadA)調用method1()。
◆ ThreadA在lock_1上同步,但允許被搶先執行。
◆ 另一個線程(ThreadB)開始執行。
◆ ThreadB調用method2()。
◆ ThreadB獲得lock_2,繼續執行,企圖獲得lock_1。但ThreadB不能獲得lock_1,因為ThreadA佔有lock_1。
◆ 現在,ThreadB阻塞,因為它在等待ThreadA釋放lock_1。
◆ 現在輪到ThreadA繼續執行。ThreadA試圖獲得lock_2,但不能成功,因為lock_2已經被ThreadB佔有了。
◆ ThreadA和ThreadB都被阻塞,程序死鎖。
當然,大多數的死鎖不會這么顯而易見,需要仔細分析代碼才能看出,對於規模較大的多線程程序來說尤其如此。好的線程分析工具,例如JProbe Threadalyzer能夠分析死鎖並指出產生問題的代碼位置。
隱性死鎖
隱性死鎖由於不規范的編程方式引起,但不一定每次測試運行時都會出現程序死鎖的情形。由於這個原因,一些隱性死鎖可能要到應用正式發布之後才會被發現,因此它的危害性比普通死鎖更大。下面介紹兩種導致隱性死鎖的情況:加鎖次序和佔有並等待。
加鎖次序
當多個並發的線程分別試圖同時佔有兩個鎖時,會出現加鎖次序沖突的情形。如果一個線程佔有了另一個線程必需的鎖,就有可能出現死鎖。考慮下面的情形,ThreadA和ThreadB兩個線程分別需要同時擁有lock_1、lock_2兩個鎖,加鎖過程可能如下:
◆ ThreadA獲得lock_1;
◆ ThreadA被搶占,VM調度程序轉到ThreadB;
◆ ThreadB獲得lock_2;
◆ ThreadB被搶占,VM調度程序轉到ThreadA;
◆ ThreadA試圖獲得lock_2,但lock_2被ThreadB佔有,所以ThreadA阻塞;
◆ 調度程序轉到ThreadB;
◆ ThreadB試圖獲得lock_1,但lock_1被ThreadA佔有,所以ThreadB阻塞;
◆ ThreadA和ThreadB死鎖。
必須指出的是,在代碼絲毫不做變動的情況下,有些時候上述死鎖過程不會出現,VM調度程序可能讓其中一個線程同時獲得lock_1和lock_2兩個鎖,即線程獲取兩個鎖的過程沒有被中斷。在這種情形下,常規的死鎖檢測很難確定錯誤所在。
佔有並等待
如果一個線程獲得了一個鎖之後還要等待來自另一個線程的通知,可能出現另一種隱性死鎖,考慮代碼二。
//代碼二
public class queue {
static java.lang.Object queueLock_;
Procer procer_;
Consumer consumer_;
public class Procer {
void proce() {
while (!done) {
「synchronized」 (queueLock_) {
proceItemAndAddItToQueue();
「synchronized」 (consumer_) {
consumer_.notify();
}
}
}
}
public class Consumer {
consume() {
while (!done) {
「synchronized」 (queueLock_) {
「synchronized」 (consumer_) {
consumer_.wait();
}
();
}
}
}
}
}
}
在代碼二中,Procer向隊列加入一項新的內容後通知Consumer,以便它處理新的內容。問題在於,Consumer可能保持加在隊列上的鎖,阻止Procer訪問隊列,甚至在Consumer等待Procer的通知時也會繼續保持鎖。這樣,由於Procer不能向隊列添加新的內容,而Consumer卻在等待Procer加入新內容的通知,結果就導致了死鎖。
在等待時佔有的鎖是一種隱性的死鎖,這是因為事情可能按照比較理想的情況發展—Procer線程不需要被Consumer占據的鎖。盡管如此,除非有絕對可靠的理由肯定Procer線程永遠不需要該鎖,否則這種編程方式仍是不安全的。有時「佔有並等待」還可能引發一連串的線程等待,例如,線程A佔有線程B需要的鎖並等待,而線程B又佔有線程C需要的鎖並等待等。
要改正代碼二的錯誤,只需修改Consumer類,把wait()移出「synchronized」()即可。
⑸ java synchronized 死鎖問題
synchronized(obj){ ... } 稱之為對obj加鎖的同步代碼塊。 你可以這么理解,每一個java對象,都具有一個鎖標記。而這個鎖標記,同時只能分配給一個線程。 有synchronized(o),意思是只有獲得o對象的鎖標記之後,後面的代碼塊才會執行,否則會等待。 回到你的例子。x1和x2是兩個不同的Xianc對象,但是,對於static的屬性來說,任何Xianc對象都共享相同的值。因此,實際上,x1的o1屬性和x2的o1屬性指向同一個對象,而x1的o2屬性和x2的o2屬性也指向相同的對象。 也就是說,Xianc.o1和Xianc.o2表示兩個對象,這兩個對象被x1和x2作為靜態屬性共享。 然後,我們開始考慮鎖標記的事情。程序中創建了兩個線程t1和t2,並首先啟動了t1線程。t1線程與x1綁定,此時執行的是flag == 0的代碼塊。 首先,遇到sync(o2),由於此時o2的鎖標記還沒有被分配,因此t1線程就能獲得o2的鎖標記,進入代碼塊。 進入代碼塊之後,接下來是sleep。由於t1線程sleep之後,釋放了cpu,導致t2線程開始運行。由於t2線程與x2綁定,此時執行的是flag == 1的代碼塊。 這時,t2線程遇到sync(o1)。由於o1的鎖標記沒有被分配,因此t2線程就能獲得o1的鎖標記,進入代碼塊。同樣的,進入代碼塊之後,t2也進入了sleep狀態,釋放了CPU。 過了一段時間,t1率先蘇醒,並被執行。但是執行過程中,會遇到syn(o1)。此時,o1的鎖標記被t2線程占據,t1無法獲得鎖標記,於是t1隻能等待。 在等待過程中,t2也蘇醒了。但是t2遇到了syn(o2),而此時o2的鎖標記被t1占據,因此t2也只能等待。 於是,兩個線程相互等待,就形成了死鎖。 手打,樓主給分
⑹ 關於Java多線程的同步鎖的問題,為什麼我搞了個死鎖老是鎖不上,大家幫我看看有沒有問題!謝謝啦
兩個問題 第一
Test a = new Test(true);
Test b = new Test(true);
這里有問題,如果都是true的話都是先鎖A後鎖B就不存在死鎖的問題
應該是
Test a = new Test(true);
Test b = new Test(false);
第二線程執行的時間不確定,經常線程A執行完了B還沒執行呢。就出現不了死鎖。需要同sleep讓線程休眠幾秒保證兩個線程都被同時執行。
改成如下就能看到死鎖了。
public class DeadLockDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自動生成的方法存根
Test a = new Test(true);
Test b = new Test(false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}
}
class Test implements Runnable{
private boolean flag;
Test(boolean flag){
this.flag=flag;
}
public void run(){
if(flag){
synchronized(MyLock.a){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"..if...a");
synchronized(MyLock.b){
System.out.println(Thread.currentThread().getName()+"..if...b");
}
}
}else{
synchronized(MyLock.b){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"..else...b");
synchronized(MyLock.a){
System.out.println(Thread.currentThread().getName()+"..else...a");
}
}
}
}
}
class MyLock{
public static Object a = new Object();
public static Object b = new Object();
}
⑺ java 同步 和 死鎖的問題 1:同步的到底是什麼synchronized() 往括弧里放入對象的含義是什麼 2:死鎖
synchronized 是同步鎖 ,,,,,一方在使用的時候、另一方等等 。。。。。。。。。。。
⑻ JAVA 線程同步 死鎖 求救
static class Iphone_Store {
private int max_num = 10;
private int min_num = 0;
private static int num = 0;
主要是消費者線程拿到的num的值還是0,沒有繼續往下走,設置成靜態的就ok了
⑼ JAVA 多線程 同步 死鎖
lock不是靜態的話,t1和t2會分別new一個出來, 不是同一個對象所以不會死鎖
⑽ 如何理解java多線程技術中的「同步鎖」和「死鎖」
正如你所說的,普通鎖就是鎖對象,靜態的就是鎖類,如果是不同對象的鎖,就一點關系沒有啊,必須是一個對象才叫線程安全啊,不然就是倆鎖,個來個的,不是一回事