锁的活跃性——死锁、活锁和饥饿

死锁

死锁现象

有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁 。

  • t1 线程 获得 A对象 锁,接下来想获取 B对象 的锁
  • t2 线程 获得 B对象 锁,接下来想获取 A对象 的锁

例:

@Slf4j(topic = "c.TestDeadLock")
public class TestDeadLock {
    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        Object A = new Object();
        Object B = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (A) {
                log.debug("lock A");
                Sleeper.sleep(1);
                synchronized (B) {
                    log.debug("Lock B");
                    log.debug("操作...");
                }
            }
        },"t1");

        Thread t2 = new Thread(() -> {
            synchronized (B) {
                log.debug("lock B");
                Sleeper.sleep(1);
                synchronized (A) {
                    log.debug("Lock A");
                    log.debug("操作...");
                }
            }
        },"t2");

        t1.start();
        t2.start();
    }
}

/*Output:
21:57:21.183 c.TestDeadLock [t1] - lock A
21:57:21.183 c.TestDeadLock [t2] - lock B
...卡住
*/
定位死锁
  • 检测死锁可以使用 jconsole工具
  • 或者使用 jps 定位进程 id,再用 jstack 定位死锁
D:\java\IdeaProjects\concurrent>jps
19476 RemoteMavenServer36
21172
22132 TestDeadLock
22364 Launcher
23132 Jps

D:\java\IdeaProjects\concurrent>jstack 22132
2020-07-11 20:50:10
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):

"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000002f13800 nid=0x5988 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t2" #12 prio=5 os_prio=0 tid=0x0000000019ee5800 nid=0x568c waiting for monitor entry [0x000000001a5ce000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.itcast.test.TestDeadLock.lambda$test1$1(TestDeadLock.java:33)
        - waiting to lock <0x00000000d6af5688> (a java.lang.Object)
        - locked <0x00000000d6af5698> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$2/1792845110.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"t1" #11 prio=5 os_prio=0 tid=0x0000000019ee4800 nid=0x19a4 waiting for monitor entry [0x000000001a4cf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.itcast.test.TestDeadLock.lambda$test1$0(TestDeadLock.java:22)
        - waiting to lock <0x00000000d6af5698> (a java.lang.Object)
        - locked <0x00000000d6af5688> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$1/897913732.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x00000000190d2000 nid=0x5ab8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x00000000190ad000 nid=0x56a0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001904d000 nid=0x2b90 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000017bae000 nid=0x5564 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000017bab800 nid=0x5418 runnable [0x000000001961e000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000000d6033b98> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000000d6033b98> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000017b63000 nid=0x594 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000018f63800 nid=0x57b8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000003008000 nid=0x11a8 in Object.wait() [0x0000000018ebf000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000d5e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000017b3a000 nid=0x3070 in Object.wait() [0x0000000018dbf000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5e06bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000d5e06bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x0000000017b17000 nid=0x44f8 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002f29800 nid=0x59e8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002f2b000 nid=0x58e4 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002f2c800 nid=0xe38 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002f2e000 nid=0xfa8 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000019124000 nid=0x5370 waiting on condition

JNI global references: 316


Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x0000000017b3e3e8 (object 0x00000000d6af5688, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x0000000017b3f1a8 (object 0x00000000d6af5698, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at cn.itcast.test.TestDeadLock.lambda$test1$1(TestDeadLock.java:33)
        - waiting to lock <0x00000000d6af5688> (a java.lang.Object)
        - locked <0x00000000d6af5698> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$2/1792845110.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"t1":
        at cn.itcast.test.TestDeadLock.lambda$test1$0(TestDeadLock.java:22)
        - waiting to lock <0x00000000d6af5698> (a java.lang.Object)
        - locked <0x00000000d6af5688> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$1/897913732.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.


D:\java\IdeaProjects\concurrent>jps
14688 Jps
22544 Launcher
19476 RemoteMavenServer36
21172
23076 TestDeadLock

D:\java\IdeaProjects\concurrent>jstack 23076
2020-07-11 22:01:28
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):

"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000003653800 nid=0x55e8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t2" #12 prio=5 os_prio=0 tid=0x000000001a30e000 nid=0x52ac waiting for monitor entry [0x000000001abdf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.itcast.test.TestDeadLock.lambda$test1$1(TestDeadLock.java:33)
        - waiting to lock <0x00000000d6af5628> (a java.lang.Object)
        - locked <0x00000000d6af5638> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$2/1792845110.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"t1" #11 prio=5 os_prio=0 tid=0x000000001a30d800 nid=0x5bdc waiting for monitor entry [0x000000001aadf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.itcast.test.TestDeadLock.lambda$test1$0(TestDeadLock.java:22)
        - waiting to lock <0x00000000d6af5638> (a java.lang.Object)
        - locked <0x00000000d6af5628> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$1/897913732.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000019785800 nid=0x5370 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000019716000 nid=0x4570 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001970b800 nid=0x4d2c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000019708000 nid=0x4d44 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000019705800 nid=0x1968 runnable [0x0000000019cde000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000000d6033628> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000000d6033628> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000182a0000 nid=0x1d2c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000195e8800 nid=0x2448 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000000374d800 nid=0x55a0 in Object.wait() [0x00000000195df000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000d5e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001827a000 nid=0x4e2c in Object.wait() [0x00000000194de000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5e06bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000d5e06bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x0000000018257000 nid=0x4358 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000003669800 nid=0x5b50 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000366b000 nid=0x1374 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000366c800 nid=0x572c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000366e000 nid=0x4d60 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000019788000 nid=0x5870 waiting on condition

JNI global references: 316


Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x0000000003749a18 (object 0x00000000d6af5628, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x000000000374bb18 (object 0x00000000d6af5638, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at cn.itcast.test.TestDeadLock.lambda$test1$1(TestDeadLock.java:33)
        - waiting to lock <0x00000000d6af5628> (a java.lang.Object)
        - locked <0x00000000d6af5638> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$2/1792845110.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"t1":
        at cn.itcast.test.TestDeadLock.lambda$test1$0(TestDeadLock.java:22)
        - waiting to lock <0x00000000d6af5638> (a java.lang.Object)
        - locked <0x00000000d6af5628> (a java.lang.Object)
        at cn.itcast.test.TestDeadLock$$Lambda$1/897913732.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.


哲学家就餐问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JiS2c2Qf-1594825203210)(https://note.youdao.com/yws/api/personal/file/D4EC23F8C6F3466F9C38920F7F91B581?method=download&shareKey=1b9eb2b9c0ce067ffaba59ccd48767e6)]

有五位哲学家,围坐在圆桌旁。

  • 他们只做两件事,思考和吃饭,思考一会吃口饭,吃完饭后接着思考。
  • 吃饭时要用两根筷子吃,桌上共有 5 根筷子,每位哲学家左右手边各有一根筷子。
  • 如果筷子被身边的人拿着,自己就得等待

代码:

public class TestDeadLock {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");
        new Philosopher("苏格拉底", c1, c2).start();
        new Philosopher("柏拉图", c2, c3).start();
        new Philosopher("亚里士多德", c3, c4).start();
        new Philosopher("赫拉克利特", c4, c5).start();
        new Philosopher("阿基米德", c5, c1).start();
    }
}

@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
    Chopstick left;
    Chopstick right;

    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true) {
            // 尝试获得左手筷子
            synchronized (left) {
                // 尝试获得右手筷子
                synchronized (right) {
                    eat();
                }
            }
        }
    }

    private void eat() {
        log.debug("eating...");       // 吃饭
        Sleeper.sleep(0.5);         // 思考
    }
}

class Chopstick {
    String name;

    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "筷子{" + name + '}';
    }
}

执行不久就会卡住,使用 jconsole 检测死锁,发现:

------------------------------------------------------------------------
名称: 阿基米德
状态: cn.itcast.n4.deadlock.v1.Chopstick@7ca7f773上的BLOCKED, 拥有者: 苏格拉底
总阻止数: 4, 总等待数: 4

堆栈跟踪: 
cn.itcast.n4.deadlock.v1.Philosopher.run(TestDeadLock.java:42)
   - 已锁定 cn.itcast.n4.deadlock.v1.Chopstick@29a2fe14
------------------------------------------------------------------------
名称: 苏格拉底
状态: cn.itcast.n4.deadlock.v1.Chopstick@ac42a13上的BLOCKED, 拥有者: 柏拉图
总阻止数: 7, 总等待数: 2

堆栈跟踪: 
cn.itcast.n4.deadlock.v1.Philosopher.run(TestDeadLock.java:42)
   - 已锁定 cn.itcast.n4.deadlock.v1.Chopstick@7ca7f773
------------------------------------------------------------------------
名称: 柏拉图
状态: cn.itcast.n4.deadlock.v1.Chopstick@5a8cec41上的BLOCKED, 拥有者: 亚里士多德
总阻止数: 4, 总等待数: 1

堆栈跟踪: 
cn.itcast.n4.deadlock.v1.Philosopher.run(TestDeadLock.java:42)
   - 已锁定 cn.itcast.n4.deadlock.v1.Chopstick@ac42a13
------------------------------------------------------------------------
名称: 亚里士多德
状态: cn.itcast.n4.deadlock.v1.Chopstick@2a73ab22上的BLOCKED, 拥有者: 赫拉克利特
总阻止数: 9, 总等待数: 3

堆栈跟踪: 
cn.itcast.n4.deadlock.v1.Philosopher.run(TestDeadLock.java:42)
   - 已锁定 cn.itcast.n4.deadlock.v1.Chopstick@5a8cec41
------------------------------------------------------------------------
名称: 赫拉克利特
状态: cn.itcast.n4.deadlock.v1.Chopstick@29a2fe14上的BLOCKED, 拥有者: 阿基米德
总阻止数: 4, 总等待数: 1

堆栈跟踪: 
cn.itcast.n4.deadlock.v1.Philosopher.run(TestDeadLock.java:42)
   - 已锁定 cn.itcast.n4.deadlock.v1.Chopstick@2a73ab22

这种线程没有按预期结束,执行不下去的情况,归类为【活跃性】问题,除了死锁以外,还有活锁和饥饿者两种情况。

活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如:

@Slf4j(topic = "c.TestLiveLock")
public class TestLiveLock {
    static volatile int count = 10;
    static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            // 期望减到 0 退出循环
            while (count > 0) {
                sleep(0.2);
                count--;
                log.debug("count: {}", count);
            }
        }, "t1").start();
        
        new Thread(() -> {
            // 期望超过 20 退出循环
            while (count < 20) {
                sleep(0.2);
                count++;
                log.debug("count: {}", count);
            }
        }, "t2").start();
    }
}

互相改变对方的结束条件,可能永远不会结束。

解决:尽量让他们的执行时间交错,例如增加随机睡眠时间。

饥饿

很多教程中把饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束,饥饿的情况不易演示,使用读写锁时会涉及饥饿问题。

解决:通过ReentrantLock

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值