如何解决java线程死锁? java线程锁定代码
当一个过程总是有一把锁,其他过程都试图得到这把锁时,它总是会被堵塞。当过程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




