java死锁的解决_java中如何造成死锁的?以及如何解决死锁

造成死锁的原因:

1.在一个代码块中同时获得多个锁,导致多个线程同时执行代码时,获取锁之间相互依赖,从而导致锁“抱死”。例如,t1线程首先获得A锁,再获得B锁,t2线程先获得B锁,再获得A锁,当t1获得A锁的同时,t2获得了B锁的使用权,此时t1无法获取B锁,t2也无法获得A锁,线程一直等待,这就叫锁“抱死”。

2.在同步代码块中调用了外部的同步方法(常见)

下面我将结合实际情况分析账户转账时,发生死锁的代码示例。

针对第一种造成死锁的原因,嵌套多个synchronized同步。

首先建一个Account类,包括账户ID,账户名称,账户余额。

public class Account {

private int id;

private String accName;

private double balance = 100;

public Account(String name) {

this.accName = name;

}

public Account(int id,String name) {

this.id = id;

this.accName = name;

}

public int compareTo(double money){

if(balance > money){

return 1;

}else if(balance == money){

return 0;

}else{

return -1;

}

}

public void in(double money){

balance += money;

System.out.println(accName+"中的余额:"+balance);

}

public void out(double money){

balance -= money;

System.out.println(accName+"中的余额:"+balance);

}

/**

* @return the accName

*/

public String getAccName() {

return accName;

}

/**

* @param accName the accName to set

*/

public void setAccName(String accName) {

this.accName = accName;

}

/**

* @return the balance

*/

public double getBalance() {

return balance;

}

/**

* @param balance the balance to set

*/

public void setBalance(double balance) {

this.balance = balance;

}

/**

* @return the id

*/

public int getId() {

return id;

}

/**

* @param id the id to set

*/

public void setId(int id) {

this.id = id;

}

}

实现转账的方法:

/**

* 造成死锁的示例:通过synchronized同步代码块

* @param A

* @param B

* @param money

* @throws Exception

*/

public static void transferMoney(Account A,Account B,double money) throws Exception{

if(A.compareTo(money) < 0){

throw new Exception("余额不足!");

}

synchronized(A){

System.out.println(Thread.currentThread().getName()+"获得了"+ A.getAccName() +"的锁");

Thread.sleep(2000);//模拟2个线程同时进行,等待获得B锁,这样就造成了死锁

synchronized (B) {

System.out.println("获得了"+ B.getAccName() +"的锁");

A.out(money);

B.in(money);

}

}

}

测试方法:

public static void main(String[] args) {

final Account[] accounts = new Account[2];

accounts[0] = new Account(1,"zhangsan");

accounts[1] = new Account(2,"lisi");

new Thread(new Runnable() {

@Override

public void run() {

try {

transferMoney(accounts[0], accounts[1], 10);

} catch (Exception e) {

e.printStackTrace();

}

}

}).start();

new Thread(new Runnable() {

@Override

public void run() {

try {

transferMoney(accounts[1], accounts[0], 10);

} catch (Exception e) {

e.printStackTrace();

}

}

}).start();

}

测试结果:

dd756b3c606a8dbb043aa341537cc9ee.png

问题的原因开头已经提到了,zhangsan账户向lis账户转账首先获取了zhangsan的对象锁,lis同时向zhangsan转账获得了lisi的对象锁,此时zhangsan无法获得zhangsan的对象锁,lisi无法获取到zhangsan的对象锁,从而导致死锁。那么问题原因知道了,如何解决呢?

通过hashcode给对象锁排序,使得每次获取锁的顺序是一致的。这里对象的hashcode一般情况下是不一样的,如果为了保险起见可以使用对象的唯一ID进行排序也是可以的。

/**方法一

* 解决死锁的方法:根据hashcode给锁排序,按照一个指定的顺序加锁

* @param A

* @param B

* @param money

* @throws Exception

*/

public static void solveDeadLock1(Account A,Account B,double money) throws Exception{

if(A.compareTo(money) < 0){

throw new Exception("余额不足!");

}

if(A.hashCode()

执行结果:

732fc91cc8df398be7548709a64ce728.png

针对第二种造成死锁的原因,同步代码块中调用外部的同步方法

转账方法修改为同步方法:

public synchronized void in(double money){

balance += money;

System.out.println(accName+"中的余额:"+balance);

}

/**

* 造成死锁的示例:通过synchronized同步方法

* @param A

* @param B

* @param money

* @throws Exception

*/

public static void transferMoney1(Account A,Account B,double money) throws Exception{

if(A.compareTo(money) < 0){

throw new Exception("余额不足!");

}

synchronized(A){

System.out.println(Thread.currentThread().getName()+"获得了"+ A.getAccName() +"的锁");

Thread.sleep(2000);//模拟2个线程同时进行,

A.out(money);

B.in(money);

}

}

测试结果:

a0c91c827b281b60c0298b97492da92b.png

原因和第一种是一样的,只是将同步代码块换成了同步方法,我们会发现zhangsan和lisi的账户中都少了10元,所以请记住一点:避免在同步代码块中调用外部的同步方法。

解决方法是尽量让原子操作的范围最小化。

实际上在转账过程中变量就是余额balance,而且balance是在in和out方法里计算的,所以将转入in方法和转出out方法都写为同步方法。

public synchronized void in(double money){

balance += money;

System.out.println(accName+"中的余额:"+balance);

}

public synchronized void out(double money){

balance -= money;

System.out.println(accName+"中的余额:"+balance);

}

/**

* 解决死锁的方法:将原子操作的范围最小化,避免在同步代码块中调用外部的同步方法

* @param A

* @param B

* @param money

* @throws Exception

*/

public static void solveDeadLock3(Account A,Account B,double money) throws Exception{

if(A.compareTo(money) < 0){

throw new Exception("余额不足!");

}

A.out(money);

B.in(money);

}

测试结果:

f26d934e0004c8c64bc6b7f320e760b0.png

总结

1.避免在同步代码块中调用外部的同步方法。

2.在嵌套多层synchronized同块中,对锁进行排序,使得每次获取锁的顺序是一致的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值