1. java中導致死鎖的原因
多個(gè)線(xiàn)程同時(shí)被阻塞,它們中的一個(gè)或者全部都在等待某個(gè)資源被釋放,而該資源又被其他線(xiàn)程鎖定,從而導致每一個(gè)線(xiàn)程都得等其它線(xiàn)程釋放其鎖定的資源,造成了所有線(xiàn)程都無(wú)法正常結束。這是從網(wǎng)上其他文檔看到的死鎖產(chǎn)生的四個(gè)必要條件:
當上述四個(gè)條件都成立的時(shí)候,便形成死鎖。當然,死鎖的情況下如果打破上述任何一個(gè)條件,便可讓死鎖消失。下面用java代碼來(lái)模擬一下死鎖的產(chǎn)生。
模擬兩個(gè)資源:
public class ThreadResource{ public static Object resource1 = new Object(); public static Object resource2 = new Object();}
模擬線(xiàn)程1占用資源1并申請獲得資源2的鎖:
public class Thread1 implements Runnable{ @Override public void run() { try { System.out.println("Thread1 is running"); synchronized (ThreadResource.resource1) { System.out.println("Thread1 lock resource1"); Thread.sleep(2000);//休眠2s等待線(xiàn)程2鎖定資源2 synchronized (ThreadResource.resource2) { System.out.println("Thread1 lock resource2"); } System.out.println("Thread1 release resource2"); } System.out.println("Thread1 release resource1"); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread1 is stop"); } }
模擬線(xiàn)程2占用資源2并申請獲得資源1的鎖:
public class Thread2 implements Runnable{ @Override public void run() { try { System.out.println("Thread2 is running"); synchronized (ThreadResource.resource2) { System.out.println("Thread2 lock resource2"); Thread.sleep(2000);//休眠2s等待線(xiàn)程1鎖定資源1 synchronized (ThreadResource.resource1) { System.out.println("Thread2 lock resource1"); } System.out.println("Thread2 release resource1"); } System.out.println("Thread2 release resource2"); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread2 is stop"); } }
同時(shí)運行倆個(gè)線(xiàn)程:
public class ThreadTest{ public static void main(String[] args) { new Thread(new Thread1()).start(); new Thread(new Thread2()).start(); }}
最后輸出結果是:
Thread1 is running
Thread2 is running
Thread1 lock resource1
Thread2 lock resource2
并且程序一直無(wú)法結束。這就是由于線(xiàn)程1占用了資源1,此時(shí)線(xiàn)程2已經(jīng)占用資源2,。這個(gè)時(shí)候線(xiàn)程1想要使用資源2,線(xiàn)程2想要使用資源1,。兩個(gè)線(xiàn)程都無(wú)法讓步,導致程序死鎖。
2. java避免死鎖的解決意見(jiàn)
由上面的例子可以看出當線(xiàn)程在同步某個(gè)對象里,再去鎖定另外一個(gè)對象的話(huà),就和容易發(fā)生死鎖的情況。最好是線(xiàn)程每次只鎖定一個(gè)對象并且在鎖定該對象的過(guò)程中不再去鎖定其他的對象,這樣就不會(huì )導致死鎖了。比如將以上的線(xiàn)程改成下面這種寫(xiě)法就可以避免死鎖:
public void run() { try { System.out.println("Thread1 is running"); synchronized (ThreadResource.resource1) { System.out.println("Thread1 lock resource1"); Thread.sleep(2000);//休眠2s等待線(xiàn)程2鎖定資源2 } System.out.println("Thread1 release resource1"); synchronized (ThreadResource.resource2) { System.out.println("Thread1 lock resource2"); } System.out.println("Thread1 release resource2"); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread1 is stop"); }
但是有的時(shí)候業(yè)務(wù)需要同時(shí)去鎖定兩個(gè)對象,比如轉賬業(yè)務(wù):A給B轉賬,需要同時(shí)鎖定A、B兩個(gè)賬戶(hù)。如果A、B相互同時(shí)轉賬的話(huà)就會(huì )出現死鎖的情況。這時(shí)可以定義一個(gè)規則:鎖定賬戶(hù)先后的規則。根據賬戶(hù)的某一個(gè)屬性(比如id或者hasCode),判斷鎖定的先后。即每一次轉賬業(yè)務(wù)都是先鎖定A再鎖定B(或者先鎖定B在鎖定A),這樣也不會(huì )導致死鎖的發(fā)生。比如按照上面的例子,需要同時(shí)鎖定兩個(gè)資源,可以根據資源的hashcode值大小來(lái)判斷先后鎖定順序??梢赃@樣改造線(xiàn)程:
public class Thread3 implements Runnable{ @Override public void run() { try { System.out.println("Thread is running"); if ( ThreadResource.resource1.hashCode() > ThreadResource.resource2.hashCode() ) { //先鎖定resource1 synchronized (ThreadResource.resource1) { System.out.println("Thread lock resource1"); Thread.sleep(2000); synchronized (ThreadResource.resource2) { System.out.println("Thread lock resource2"); } System.out.println("Thread release resource2"); } System.out.println("Thread release resource1"); } else { //先鎖定resource2 synchronized (ThreadResource.resource2) { System.out.println("Thread lock resource2"); Thread.sleep(2000); synchronized (ThreadResource.resource1) { System.out.println("Thread lock resource1"); } System.out.println("Thread release resource1"); } System.out.println("Thread release resource2"); } } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread1 is stop"); } }
總結:死鎖常見(jiàn)于,線(xiàn)程在鎖定對象還沒(méi)釋放時(shí),又需要鎖定另一個(gè)對象,并且此時(shí)該對象可能被另一個(gè)線(xiàn)程鎖定。這種時(shí)候很容易導致死鎖。因此在開(kāi)發(fā)時(shí)需要慎重使用鎖,尤其是需要注意盡量不要在鎖里又加鎖。
注意:本文僅代表個(gè)人理解和看法喲!和本人所在公司和團體無(wú)任何關(guān)系!
聯(lián)系客服