如何解决java线程死锁? java线程锁定代码

2023-06-14

当一个过程总是有一把锁,其他过程都试图得到这把锁时,它总是会被堵塞。当过程A有锁定L并想获得锁定M时,过程B有锁定M并试图获得锁定L时,那么这两个过程将永远等待。这种情况是一种简单的锁定方式,其中许多过程总是等待,因为环路的锁定依赖,所以有一把锁。


1、锁顺序死锁


以下是顺序锁的一个例子,代码如下:


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有left的锁,并且尝试得到right的锁,而过程t2有right的锁,并且尝试得到left的锁,因此产生死锁。死锁产生的原因是:两个过程尝试以不同的顺序获得相同的锁,如果按相同的顺序要求锁定,则不会出现循环上锁依赖,因此也不会产生死锁。


假如所有的过程都按固定的顺序获取锁,那么在系统中就不会出现锁定顺序死锁的问题。


2、动态锁定顺序死锁


有时,为了避免死锁的发生,我们无法清楚地知道锁定顺序是否有足够的控制权,看看下面的转帐代码。


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


以上代码是资金从一个账户转移到另一个账户的简单实现。在开始转账之前,您应该获得这两个Account对象的锁,以确保两个账户的余额通过原子更新。看起来所有的过程都是按照顺序锁来获取的,但实际上,锁的顺序取决于转移函数transferMoney参数的顺序,而这些参数则取决于外部输入,如果两个过程同时调用transferMoney,其中一个过程从X转移到Y,另一个过程从Y转移到X转移,那么死锁就有可能发生:


进程A:transferMoney(xAccount, yAccount);


进程B:transferMoney(yAccount, xAccount);


为避免这种情况发生,必须按顺序进行锁定。下面的代码:


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


在极少数情况下,两个目标可能有相对的hashCode值。此时,需要添加额外的锁。在获得两个Account的锁之前,您必须首先获得这个额外的锁。然后清除死锁。


3、避免和诊断死锁


(1) 假如一个程序每次最多只能得到一把锁,那么锁的顺序就不会被锁定。


(2) 若要得到多个锁,则在设计时必须考虑锁的顺序,若按固定顺序得到锁,则不会出现锁的顺序。


(3)支持定时锁,例如在Lock类中显示使用定时tryLock功能代替内置锁,显示锁可以指定一个超时限制,在等待超过一定时间后,tryLock将返回失败信息。






本文为转载内容,我们尊重原作者对本文的作权。如有内容错误或侵权问题,欢迎原作者联系我们更正或删除内容。

本文仅代表作者观点,版权归原创者所有,如需转载请在文中注明来源及作者名字。

免责声明:本文系转载编辑文章,仅作分享之用。如分享内容、图片侵犯到您的版权或非授权发布,请及时与我们联系进行审核处理或删除,您可以发送材料至邮箱:service@tojoy.com