如何解決java線程死鎖? java線程鎖定代碼
當(dāng)一個(gè)過程總是有一把鎖,其他過程都試圖得到這把鎖時(shí),它總是會被堵塞。當(dāng)過程A有鎖定L并想獲得鎖定M時(shí),過程B有鎖定M并試圖獲得鎖定L時(shí),那么這兩個(gè)過程將永遠(yuǎn)等待。這種情況是一種簡單的鎖定方式,其中許多過程總是等待,因?yàn)榄h(huán)路的鎖定依賴,所以有一把鎖。
1、鎖順序死鎖
以下是順序鎖的一個(gè)例子,代碼如下:
1 package deadLock;
2
3 public class LeftRightDeadLock {
4 private final Object left = new Object();
5 private final Object right = new Object();
6
7 public void leftRight() throws Exception{
8 synchronized (left) {
9 Thread.sleep(2000);
10 synchronized (right) {
11 System.out.println("left to right");
12 }
13 }
14 }
15
16
17 public void rightLeft() throws Exception{
18 synchronized (right) {
19 Thread.sleep(2000);
20 synchronized (left) {
21 System.out.println("right to left");
22 }
23 }
24 }
25
26 }
View Code
1 package deadLock;
2
3 public class LeftRightThread extends Thread {
4
5 private LeftRightDeadLock d;
6 public LeftRightThread(LeftRightDeadLock d){
7 this.d = d;
8 }
9 @Override
10 public void run() {
11 try{
12 d.leftRight();
13 }catch(Exception ex){
14 ex.printStackTrace();
15 }
16 }
17
18 }
View Code
1 package deadLock;
2
3 public class RightLeftThread extends Thread {
4
5 private LeftRightDeadLock d;
6 public RightLeftThread(LeftRightDeadLock d){
7 this.d = d;
8 }
9 @Override
10 public void run() {
11 try{
12 d.rightLeft();
13 }catch(Exception ex){
14 ex.printStackTrace();
15 }
16 }
17
18 }
View Code
1 package deadLock;
2
3 public class Main {
4 public static void main(String[] args) {
5 LeftRightDeadLock d = new LeftRightDeadLock();
6 LeftRightThread t1 = new LeftRightThread(d);
7 RightLeftThread t2 = new RightLeftThread(d);
8 t1.start();
9 t2.start();
10 }
11 }
View Code
過程t1有l(wèi)eft的鎖,并且嘗試得到right的鎖,而過程t2有right的鎖,并且嘗試得到left的鎖,因此產(chǎn)生死鎖。死鎖產(chǎn)生的原因是:兩個(gè)過程嘗試以不同的順序獲得相同的鎖,如果按相同的順序要求鎖定,則不會出現(xiàn)循環(huán)上鎖依賴,因此也不會產(chǎn)生死鎖。
假如所有的過程都按固定的順序獲取鎖,那么在系統(tǒng)中就不會出現(xiàn)鎖定順序死鎖的問題。
2、動(dòng)態(tài)鎖定順序死鎖
有時(shí),為了避免死鎖的發(fā)生,我們無法清楚地知道鎖定順序是否有足夠的控制權(quán),看看下面的轉(zhuǎn)帳代碼。
1 public class TransferAccounts {
2 public void transferMoney(Account fromAccount, Account toAccount, double amount) throws Exception{
3 synchronized (fromAccount) {
4 synchronized (toAccount) {
5 if(fromAccount.getBalance() - amount < 0){
6 throw new Exception();
7 }
8 else{
9 fromAccount.setBalance(amount);
10 toAccount.add(amount);
11 }
12 }
13 }
14 }
15 }
View Code
1 public class Account {
2
3 //額度
4 private double balance;
5
6 public double getBalance() {
7 return balance;
8 }
9
10 public void setBalance(double balance) {
11 this.balance = balance;
12 }
13 public void add(double amount){
14 balance = amount;
15 }
16 public void subtra(double amount){
17 balance -= amount;
18 }
19
20 }
View Code
以上代碼是資金從一個(gè)賬戶轉(zhuǎn)移到另一個(gè)賬戶的簡單實(shí)現(xiàn)。在開始轉(zhuǎn)賬之前,您應(yīng)該獲得這兩個(gè)Account對象的鎖,以確保兩個(gè)賬戶的余額通過原子更新??雌饋硭械倪^程都是按照順序鎖來獲取的,但實(shí)際上,鎖的順序取決于轉(zhuǎn)移函數(shù)transferMoney參數(shù)的順序,而這些參數(shù)則取決于外部輸入,如果兩個(gè)過程同時(shí)調(diào)用transferMoney,其中一個(gè)過程從X轉(zhuǎn)移到Y(jié),另一個(gè)過程從Y轉(zhuǎn)移到X轉(zhuǎn)移,那么死鎖就有可能發(fā)生:
進(jìn)程A:transferMoney(xAccount, yAccount);
進(jìn)程B:transferMoney(yAccount, xAccount);
為避免這種情況發(fā)生,必須按順序進(jìn)行鎖定。下面的代碼:
1 public class TransferAccounts {
2 private static final Object tieLock = new Object();
3
4 public void transfer(Account fromAccount, Account toAccount,
5 double amount) throws Exception {
6 if (fromAccount.getBalance() - amount < 0) {
7 throw new Exception();
8 } else {
9 fromAccount.setBalance(amount);
10 toAccount.add(amount);
11 }
12 }
13 public void transferMoney(Account fromAccount, Account toAccount,
14 double amount) throws Exception{
15 int fromHash = fromAccount.hashCode();
16 int toHash = toAccount.hashCode();
17 if(fromHash < toHash){
18 synchronized (fromAccount) {
19 synchronized (toAccount) {
20 transfer(fromAccount, toAccount, amount);
21 }
22 }
23 }else if(fromHash > toHash){
24 synchronized (toAccount) {
25 synchronized (fromAccount) {
26 transfer(fromAccount, toAccount, amount);
27 }
28 }
29 }else {
30 synchronized (tieLock) {
31 synchronized (fromAccount) {
32 synchronized (toAccount) {
33 transfer(fromAccount, toAccount, amount);
34 }
35 }
36 }
37 }
38 }
39
40 }
View Code
在極少數(shù)情況下,兩個(gè)目標(biāo)可能有相對的hashCode值。此時(shí),需要添加額外的鎖。在獲得兩個(gè)Account的鎖之前,您必須首先獲得這個(gè)額外的鎖。然后清除死鎖。
3、避免和診斷死鎖
(1) 假如一個(gè)程序每次最多只能得到一把鎖,那么鎖的順序就不會被鎖定。
(2) 若要得到多個(gè)鎖,則在設(shè)計(jì)時(shí)必須考慮鎖的順序,若按固定順序得到鎖,則不會出現(xiàn)鎖的順序。
(3)支持定時(shí)鎖,例如在Lock類中顯示使用定時(shí)tryLock功能代替內(nèi)置鎖,顯示鎖可以指定一個(gè)超時(shí)限制,在等待超過一定時(shí)間后,tryLock將返回失敗信息。
本文僅代表作者觀點(diǎn),版權(quán)歸原創(chuàng)者所有,如需轉(zhuǎn)載請?jiān)谖闹凶⒚鱽碓醇白髡呙帧?/p>
免責(zé)聲明:本文系轉(zhuǎn)載編輯文章,僅作分享之用。如分享內(nèi)容、圖片侵犯到您的版權(quán)或非授權(quán)發(fā)布,請及時(shí)與我們聯(lián)系進(jìn)行審核處理或刪除,您可以發(fā)送材料至郵箱:service@tojoy.com