并发编程之死锁

死锁

死锁就是两个或以上线程互相争抢对方资源,导致没有任何一个线程能获得所需所有资源的一种僵持状态。死锁下的线程是 BLOCKED 状态。

下面我们来使用Synchronized 关键字 造一个死锁。

ServiceOne类:

public class ServiceOne {
    private ServiceTwo serviceTwo;
    private final Object LOCK_ONE = new Object();

    public void setServiceTwo(ServiceTwo serviceTwo) {
        this.serviceTwo = serviceTwo;
    }

    public void soMethod1(){
        synchronized (LOCK_ONE){
            System.out.println("soMethod1 get LOCK_ONE.");
            System.out.println("handle soMethod1.");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            serviceTwo.stMethod1();
        }
    }

    public void soMethod2(){
        synchronized (LOCK_ONE){
            System.out.println("soMethod2 get LOCK_ONE.");
            System.out.println("handle soMethod2.");
        }
    }
}

ServiceTwo类:

public class ServiceTwo {
    private ServiceOne serviceOne;
    private final Object LOCK_TWO = new Object();

    public void setServiceOne(ServiceOne serviceOne) {
        this.serviceOne = serviceOne;
    }

    public void stMethod1() {
        synchronized (LOCK_TWO){
            System.out.println("stMethod1 get LOCK_TWO");
            System.out.println("handle.");
        }
    }

    public void stMethod2(){
        synchronized (LOCK_TWO){
            System.out.println("stMethod2 get LOCK_TWO.");
            System.out.println("handle stMethod2.");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            serviceOne.soMethod2();
        }
    }
}

测试代码:

public class TestService {

    public static void main(String[] args) {
        ServiceOne serviceOne = new ServiceOne();
        ServiceTwo serviceTwo = new ServiceTwo();
        serviceOne.setServiceTwo(serviceTwo);
        serviceTwo.setServiceOne(serviceOne);

        new Thread(){
            @Override
            public void run() {
                serviceOne.soMethod1();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                serviceTwo.stMethod2();
            }
        }.start();
    }
}

输出结果:

soMethod1 get LOCK_ONE.
handle soMethod1.
stMethod2 get LOCK_TWO.
handle stMethod2.

然后线程一直等待。

分析:

首先我们假设线程1和线程2同时启动(虽然不是同时启动,但是相差远小于1s)

在这里插入图片描述

通过上图可以知道,线程1持有LOCK_ONE, 想获取LOCK_TWO, 而线程2持有LOCK_TWO, 想获取LOCK_ONE, 所以导致了死锁。

下面我们通过 jstack 查看线程情况。

首先使用命令jps 查询线程。

F:\1SourceCode\java-concurrency> jps
12036 Jps
12600
22824 TestService
8104 org.eclipse.equinox.launcher_1.5.700.v20200207-2156.jar
11196 Launcher

然后使用jstack

F:\1SourceCode\java-concurrency>jstack 22824
2020-05-15 21:56:39
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.91-b15 mixed mode):

...

"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000002f62800 nid=0x5eac waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001ebc9000 nid=0x6248 waiting for monitor entry [0x000000001f99e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at org.example.chapter8.deadLockDemo.ServiceOne.soMethod2(ServiceOne.java:30)
        - waiting to lock <0x000000076b3b44f8> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.ServiceTwo.stMethod2(ServiceTwo.java:27)
        - locked <0x000000076b3b7a20> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.TestService$2.run(TestService.java:20)

"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001ebc8000 nid=0x4584 waiting for monitor entry [0x000000001f89f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at org.example.chapter8.deadLockDemo.ServiceTwo.stMethod1(ServiceTwo.java:13)
        - waiting to lock <0x000000076b3b7a20> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.ServiceOne.soMethod1(ServiceOne.java:23)
        - locked <0x000000076b3b44f8> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.TestService$1.run(TestService.java:14)

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001eb15800 nid=0x6320 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
...
JNI global references: 362


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000000305dff8 (object 0x000000076b3b44f8, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000000305b768 (object 0x000000076b3b7a20, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at org.example.chapter8.deadLockDemo.ServiceOne.soMethod2(ServiceOne.java:30)
        - waiting to lock <0x000000076b3b44f8> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.ServiceTwo.stMethod2(ServiceTwo.java:27)
        - locked <0x000000076b3b7a20> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.TestService$2.run(TestService.java:20)
"Thread-0":
        at org.example.chapter8.deadLockDemo.ServiceTwo.stMethod1(ServiceTwo.java:13)
        - waiting to lock <0x000000076b3b7a20> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.ServiceOne.soMethod1(ServiceOne.java:23)
        - locked <0x000000076b3b44f8> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.TestService$1.run(TestService.java:14)

Found 1 deadlock.

可以看到 Found 1 deadlock. 表示发现一个死锁。我们再拿出我们创建的 Thread-0Thread-1 两个线程信息分析:

"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001ebc9000 nid=0x6248 waiting for monitor entry [0x000000001f99e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at org.example.chapter8.deadLockDemo.ServiceOne.soMethod2(ServiceOne.java:30)
        - waiting to lock <0x000000076b3b44f8> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.ServiceTwo.stMethod2(ServiceTwo.java:27)
        - locked <0x000000076b3b7a20> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.TestService$2.run(TestService.java:20)

"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001ebc8000 nid=0x4584 waiting for monitor entry [0x000000001f89f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at org.example.chapter8.deadLockDemo.ServiceTwo.stMethod1(ServiceTwo.java:13)
        - waiting to lock <0x000000076b3b7a20> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.ServiceOne.soMethod1(ServiceOne.java:23)
        - locked <0x000000076b3b44f8> (a java.lang.Object)
        at org.example.chapter8.deadLockDemo.TestService$1.run(TestService.java:14)

可以看到 Thread-1 locked <0x000000076b3b7a20> , waiting to lock <0x000000076b3b44f8>, 而 Thread-0 locked <0x000000076b3b44f8> , waiting to lock <0x000000076b3b7a20> , 这里也可以明显的看到锁的信息。 并且都处于BLOCKED状态。所以死锁情况下的线程处于 BLOCKED .

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页