您当前的位置: 首页 >  Java

郭梧悠

暂无认证

  • 0浏览

    0关注

    402博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java并发编程实战读书笔记之死锁

郭梧悠 发布时间:2021-11-27 10:06:52 ,浏览量:0

死锁的分类
  • 前言
  • 一、锁顺序死锁
  • 二、动态的锁顺序死锁。

提示:以下是本篇文章正文内容,下面案例可供参考

一、锁顺序死锁

看下面代码,很容易造成死锁,leftRight和rightLeft方法分别获取了left和right锁,只不过二者获取锁的顺序是相反的。那么如果一个Thread调用了leftRight,另一个Thread调用了rightLeft,并且两个Thread的操作是交错执行的,那么就容易发生时所。


class LeftRightDeadlock {
	private final Object left = new Object();
	private final Object right = new Object();
	
	public void leftRight() {
		synchronized(left) {
			synchronized(right) {
				doSomethingA();
			}
		}
	}
	
	public void rightLeft() {
		synchronized(right) {
			synchronized(left) {
				doSomethingB();
			}
		}
	}
}

如下调用就能发生死锁

ThreadA :----->锁住left---->尝试锁住right--->永久等待。
ThreadB :----->锁住right--->尝试锁住left--->永久等待。

根本原因在于leftRight和rightLeft方法获取锁的顺序不一样。如果每个需要left锁和right锁的线程都以相同的顺序来获取left锁和right锁则不存存在此问题。所以如果所有线程以固定的顺序来获取锁,那么在程序中就不会存在因为顺序造成的死锁问题。

所以我们在这里将rightLeft改成跟leftRight一样的顺序即可:

public void rightLeft() {
		synchronized(left) {
			synchronized(right) {
				doSomethingB();
			}
		}
	}
二、动态的锁顺序死锁。

leftRight和rightLeft方法是两个固定写死的方法,所以修改他们很容易,只需要将顺序改变成一致即可。但是,如果是下面的代码呢?是否会发生死锁呢?

	/**
	 * 转账方法
	 * @param fromAccount 谁转账
	 * @param toAccount  转账给谁
	 * @param amount 转账金额
	 */
	public void transferMoneyMoney(Account fromAccount,Account toAccount,DollarAmount amount) {
		synchronized(fromAccount) {
			synchronized(toAccount) {
				//执行转账逻辑
			}
		}
		
	}

transferMoneyMoney方法的作用就是用来转账,参数定了转账账户、被转账账户和转账金额。乍一看没问题,但是仍然会造成死锁,死锁的原因跟leftRight和rightLeft一模一样,都是获取锁的顺序造成的:

ThreadA :transferMoneyMoney(X,Y)----->锁住X---->尝试锁住Y-->永久等待。
ThreadB :transferMoneyMoney(Y,X)------>锁住Y--->尝试锁住X--->永久等待。

前面说过只要所有线程以固定的顺序获取锁,那么就不会发生死锁,所以我们改造的方向也有了,就是要达成如下效果,也就是获取所得顺序不应该跟参数的顺序有关,而是固定顺序:

ThreadA :transferMoneyMoney(X,Y)----->锁住X---->尝试锁住Y-->转账成功。
ThreadB :transferMoneyMoney(Y,X)------>锁住X--->尝试锁住Y--->转账成功。

有了这个思路我们就可以如下改造:

	/**
	 * 转账方法
	 * @param fromAccount 谁转账
	 * @param toAccount  转账给谁
	 * @param amount 转账金额
	 */
	public void transferMoneyMoney(Account fromAccount,Account toAccount,DollarAmount amount) {
		int fromHash = System.identityHashCode(fromAccount);
		int toHash =  System.identityHashCode(toHash);
		
		if(fromHash>toHash) {
			synchronized(fromAccount) {
				synchronized(toAccount) {
					//执行转账逻辑
				}
			}
		}else if(fromHashyHash:

ThreadA :transferMoneyMoney(X,Y)----->锁住X---->尝试锁住Y-->转账成功。
ThreadB :transferMoneyMoney(Y,X)------>锁住X--->尝试锁住Y--->转账成功。

如果xHash锁住Y---->尝试锁住X-->转账成功。 ThreadB :transferMoneyMoney(Y,X)------>锁住Y--->尝试锁住X--->转账成功。

可以看出不管fromAccount和toAccount的两个参数怎么调换,都能保证ThreadA和ThreadB获取锁的顺序是一致的,不会造成死锁问题。

下面在处理下fromAccount.hashCode和toAccount.hashCode相等的情况,这个情况好判断,在加一把锁就可以了:

   /**
	 * 转账方法
	 * @param fromAccount 谁转账
	 * @param toAccount  转账给谁
	 * @param amount 转账金额
	 */
	private static final Object lock = new Object(); 
	public void transferMoneyMoney(Account fromAccount,Account toAccount,DollarAmount amount) {
		if(fromHash>toHash) {	
		}else if(fromHash            
关注
打赏
1663674776
查看更多评论
0.0395s