死锁问题的出现和解决

什么是死锁

死锁是多线程编程中会遇到的一个问题,下面就先来讲一下什么是死锁。

首先,大家都知道的是,当一个线程持有一个锁的时候,其他尝试获取这个锁的线程都会阻塞。

那么如果,线程A持有了锁A,并且尝试获取锁B,线程B持有了锁B,并且尝试获取锁A。那么这两个线程将会永远阻塞下去,这就是最简单的死锁。

示例

下面给出简单的死锁代码实现:

public class DeadLockTest {
    public static void main(String[] args) {
        DeadLock deadLock = new DeadLock();
        new Thread(new Runnable() {

            @Override
            public void run() {
                deadLock.methodA();
            }
        }, "Thread-A").start();
        new Thread(new Runnable() {

            @Override
            public void run() {
                deadLock.methodB();
            }
        }, "Thread-B").start();
    }
}

class DeadLock {

    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized (lockA) {
            System.out.println("methodA in");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB) {
                System.out.println("methodA out");
            }
        }
    }

    public void methodB() {
        synchronized (lockB) {
            System.out.println("methodB in");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockA) {
                System.out.println("methodB out");
            }
        }
    }

}

可以注意的是,由于线程抢占CPU的不确定性,所以我们在代码中调用Thread.sleep(1000)让线程停了一秒钟。

上面代码执行后控制台输出:
死锁
- 由于程序一直没有停止,所以程序肯定进入了死锁
- 由于methodA和methodB都只打印”in”而没有打印”out”,所以Thread-A和Thread-B分别在methodA和methodB中阻塞了

jstack分析死锁

下面我们再使用jstack打印堆栈信息来确认一下是否发生死锁
- 1.使用jps获取main方法Java虚拟机pid

C:\Users\lebron>jps
5360 Jps
5820
8188 DeadLockTest
  • 2.jstack打印堆栈信息
C:\Users\lebron>jstack 8188
2018-07-24 16:21:24
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):

Found one Java-level deadlock:
=============================
"Thread-B":
  waiting to lock monitor 0x00000000177997b8 (object 0x00000000d6adf598, a java.util.ArrayList),
  which is held by "Thread-A"
"Thread-A":
  waiting to lock monitor 0x0000000017795718 (object 0x00000000d6adf750, a java.util.HashSet),
  which is held by "Thread-B"

Java stack information for the threads listed above:
===================================================
"Thread-B":
        at com.tvunetworks.wedding.device.DeadLock.methodB(DeadLockTest.java:55)
        - waiting to lock <0x00000000d6adf598> (a java.util.ArrayList)
        - locked <0x00000000d6adf750> (a java.util.HashSet)
        at com.tvunetworks.wedding.device.DeadLockTest$2.run(DeadLockTest.java:21)
        at java.lang.Thread.run(Thread.java:748)
"Thread-A":
        at com.tvunetworks.wedding.device.DeadLock.methodA(DeadLockTest.java:41)
        - waiting to lock <0x00000000d6adf750> (a java.util.HashSet)
        - locked <0x00000000d6adf598> (a java.util.ArrayList)
        at com.tvunetworks.wedding.device.DeadLockTest$1.run(DeadLockTest.java:14)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

这里只截取了部分jstack信息,通过堆栈信息,可以很明确的看到Thread-B在尝试获取锁A,Thread-A在尝试获取锁B,并且发生了一个死锁。

死锁的危害

  • 导致程序得不到正确的结果:因为程序产生思索,发生阻塞,不会继续向下执行
  • 浪费资源:产生死锁,最少有两个线程会发生阻塞
  • 产生新的死锁:产生死锁的线程会一直占有着锁资源,会导致其他尝试获取该锁的线程也发生死锁,产生多米诺效应

规避死锁

既然产生死锁的危害这么大,那我们要怎么规避死锁呢
- 方法开始前一次性申请所需要的资源
- 当线程申请新的资源不被满足的时候,释放已占有的资源,下次需要时在重新申请
- 对系统中需要申请的资源进行线性排序,规定加锁顺序

喜欢文章的,扫码关注微信公众号
扫码关注

猜你喜欢

转载自blog.csdn.net/Leon_cx/article/details/81196962
今日推荐