一文带你了解死锁

什么是死锁

死锁就是两个或多个线程并行执行时,彼此等待对方持有的资源,从而形成了一种僵持状态。

死锁形成有4个条件:

  • 互斥
  • 持有资源并等待
  • 不可剥夺
  • 环路等待

示例

我们创建两个线程,分别为线程A和线程B。
在线程A中,将获取资源A,睡眠一秒,然后获取资源B。
在线程B中,将获取资源B,睡眠一秒,然后获取资源A。

代码示例如下:

public class DeadLockDemo {
    
    

	private static Object resourceA = new Object();
	private static Object resourceB = new Object();

	public static void main(String[] args) {
    
    
		Thread threadA = createThreadA();
		Thread threadB = createThreadB();
		threadA.start();
		threadB.start();
	}

	private static Thread createThreadA() {
    
    
		Thread threadA = new Thread(() -> {
    
    
			synchronized (resourceA) {
    
    
				System.out.println(Thread.currentThread() + " got ResourceA");
				try {
    
    
					Thread.sleep(1000);
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
				}

				System.out.println(Thread.currentThread() + " waiting get ResourceB");
				synchronized (resourceB) {
    
    
					System.out.println(Thread.currentThread() + " got ResourceB");
				}
			}
		}, "ThreadA");
		return threadA;
	}

	private static Thread createThreadB() {
    
    
		Thread threadA = new Thread(() -> {
    
    
			synchronized (resourceB) {
    
    
				System.out.println(Thread.currentThread() + " got ResourceB");
				try {
    
    
					Thread.sleep(1000);
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
				}

				System.out.println(Thread.currentThread() + " waiting get ResourceA");
				synchronized (resourceA) {
    
    
					System.out.println(Thread.currentThread() + " got ResourceA");
				}
			}
		}, "ThreadB");
		return threadA;
	}
}
  • 执行结果
Thread[ThreadA,5,main] got ResourceA
Thread[ThreadB,5,main] got ResourceB
Thread[ThreadB,5,main] waiting get ResourceA
Thread[ThreadA,5,main] waiting get ResourceB

工具检测

jstack命令可以用来检测死锁信息。
执行命令 jstack <PID>, 得到相关信息如下:

Found one Java-level deadlock:
=============================
"ThreadB":
  waiting to lock monitor 0x00007f97a3002cb8 (object 0x000000076ac74998, a java.lang.Object),
  which is held by "ThreadA"
"ThreadA":
  waiting to lock monitor 0x00007f97a3005548 (object 0x000000076ac749a8, a java.lang.Object),
  which is held by "ThreadB"

Java stack information for the threads listed above:
===================================================
"ThreadB":
	at com.hornsey.learn.concurrent.DeadLockDemo.lambda$createThreadB$1(DeadLockDemo.java:54)
	- waiting to lock <0x000000076ac74998> (a java.lang.Object)
	- locked <0x000000076ac749a8> (a java.lang.Object)
	at com.hornsey.learn.concurrent.DeadLockDemo$$Lambda$2/1706234378.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
"ThreadA":
	at com.hornsey.learn.concurrent.DeadLockDemo.lambda$createThreadA$0(DeadLockDemo.java:35)
	- waiting to lock <0x000000076ac749a8> (a java.lang.Object)
	- locked <0x000000076ac74998> (a java.lang.Object)
	at com.hornsey.learn.concurrent.DeadLockDemo$$Lambda$1/1198108795.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

通过jstack的输出结果可知,线程A在等待线程B持有的锁,线程B在等待线程A持有的锁,结果形成了死锁。

如何解决

资源有序分配法

前面讲到,死锁形成有4个条件。我们只想破坏其中一个条件,就可以避免死锁。场景的解决死锁的办法就是资源有序分配法,来破坏死锁的环路条件。
比如上面的例子中,线程B获取资源的顺序改成同样先获取资源A,再获取资源B,则程序就可以正常执行了。

执行结果如下:

Thread[ThreadA,5,main] got ResourceA
Thread[ThreadA,5,main] waiting get ResourceB
Thread[ThreadA,5,main] got ResourceB
Thread[ThreadB,5,main] got ResourceA
Thread[ThreadB,5,main] waiting get ResourceB
Thread[ThreadB,5,main] got ResourceB

猜你喜欢

转载自blog.csdn.net/nalw2012/article/details/114491941