线程死锁

一、死锁

      何为死锁,用一个进餐场景来描述,一桌子饭,5个人吃,5根筷子,每人面前一根,假如刚好有一种算法是每个人立即拿起一根筷子,同时又等待你右侧的人放下筷子,你成一双,然后吃饭,这样大家都会饿死,这就解释了死锁的现象。两个线程死锁也是同样的道理,线程A持有M锁,而等待L锁释放,线程B持有L锁,但等待M锁释放,于是就发生了死锁。

1.1、顺序死锁

	public class LeftRightDeadlock {
		    private final Object left = new Object();
		    private final Object right = new Object();

		    public void leftRight() {
		        // 得到left锁
		        synchronized (left) {
		            // 得到right锁
		            synchronized (right) {
		                doSomething();
		            }
		        }
		    }

		    public void rightLeft() {
		        // 得到right锁
		        synchronized (right) {
		            // 得到left锁
		            synchronized (left) {
		                doSomethingElse();
		            }
		        }
		    }
		}

 

  • 线程A调用leftRight()方法,得到left锁
  • 同时线程B调用rightLeft()方法,得到right锁
  • 线程A和线程B都继续执行,此时线程A需要right锁才能继续往下执行。此时线程B需要left锁才能继续往下执行。
  • 但是:线程A的left锁并没有释放,线程B的right锁也没有释放
  • 所以他们都只能等待,而这种等待是无期限的-->永久等待-->死锁

1.2动态锁顺序死锁

public static void transferMoney(Account fromAccount,
                Account toAccount,
                DollarAmount amount)
                        throws InsufficientFundsException {

            // 锁定汇账账户
            synchronized (fromAccount) {
                // 锁定来账账户
                synchronized (toAccount) {

                    // 判余额是否大于0
                    if (fromAccount.getBalance().compareTo(amount) < 0) {
                            throw new InsufficientFundsException();
                    } else {

                            // 汇账账户减钱
                        fromAccount.debit(amount);

                            // 来账账户增钱
                        toAccount.credit(amount);
                    }
                }

}

 

  • 如果两个线程同时调用transferMoney()
  • 线程A从X账户向Y账户转账
  • 线程B从账户Y向账户X转账
  • 那么就会发生死锁。
  • A:transferMoney(myAccount,yourAccount,10);
    
    B:transferMoney(yourAccount,myAccount,20);

 

1.3协作对象之间发生死锁

public class CooperatingDeadlock {
    // Warning: deadlock-prone!
    class Taxi {
        @GuardedBy("this") private Point location, destination;
        private final Dispatcher dispatcher;

        public Taxi(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
        }

        public synchronized Point getLocation() {
            return location;
        }

        // setLocation 需要Taxi内置锁
        public synchronized void setLocation(Point location) {
            this.location = location;
            if (location.equals(destination))
                // 调用notifyAvailable()需要Dispatcher内置锁
                dispatcher.notifyAvailable(this);
        }

        public synchronized Point getDestination() {
            return destination;
        }

        public synchronized void setDestination(Point destination) {
            this.destination = destination;
        }
    }

    class Dispatcher {
        @GuardedBy("this") private final Set<Taxi> taxis;
        @GuardedBy("this") private final Set<Taxi> availableTaxis;

        public Dispatcher() {
            taxis = new HashSet<Taxi>();
            availableTaxis = new HashSet<Taxi>();
        }

        public synchronized void notifyAvailable(Taxi taxi) {
            availableTaxis.add(taxi);
        }

        // 调用getImage()需要Dispatcher内置锁
        public synchronized Image getImage() {
            Image image = new Image();
            for (Taxi t : taxis)
                // 调用getLocation()需要Taxi内置锁
                image.drawMarker(t.getLocation());
            return image;
        }
    }

    class Image {
        public void drawMarker(Point p) {
        }
    }
}

 

  • 并且在操作途中是没有释放锁的

这就是隐式获取两个锁(对象之间协作)..

这种方式也很容易就造成死锁.....

二、避免死锁的方法

  • 固定加锁的顺序(针对锁顺序死锁)
  • 开放调用(针对对象之间协作造成的死锁)
  • 使用定时锁-->tryLock()

2.4死锁检测

JDK提供了两种方式来给我们检测:

  • JconsoleJDK自带的图形化界面工具,使用JDK给我们的的工具JConsole
  • Jstack是JDK自带的命令行工具,主要用于线程Dump分析。
  • 具体参考:死锁检测

发生死锁的原因主要由于:

  • 线程之间交错执行
    • 解决:以固定的顺序加锁
  • 执行某方法时就需要持有锁,且不释放
    • 解决:缩减同步代码块范围,最好仅操作共享变量时才加锁
  • 永久等待
    • 解决:使用tryLock()定时锁,超过时限则返回错误信息

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值