一、锁细化

一间大屋子有两个功能:睡觉、学习,互不相干。
现在小明要学习,小红要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低

public class TestMultiLock {
    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            try {
                bigRoom.study();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"小明").start();
        new Thread(() -> {
            try {
                bigRoom.sleep();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"小红").start();
    }
}
@Slf4j
class BigRoom {
    public void sleep() throws InterruptedException {
        synchronized (this) {
            log.debug("睡 2 小时");
            Thread.sleep(2000);
        }
    }

    public void study() throws InterruptedException {
        synchronized (this) {
            log.debug("学 1 小时");
            Thread.sleep(1000);
        }
    }
}
18:38:46.400 [小明] DEBUG juc.lock.BigRoom - 学 1 小时
18:38:47.403 [小红] DEBUG juc.lock.BigRoom - 睡 2 小时

解决方法:准备多个房间(多个对象锁)

public class TestMultiLock {
    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            try {
                bigRoom.study();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"小明").start();
        new Thread(() -> {
            try {
                bigRoom.sleep();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"小红").start();
    }
}
@Slf4j
class BigRoom {
    private final Object studyRoom = new Object();
    private final Object bedroom = new Object();

    public void sleep() throws InterruptedException {
        synchronized (bedroom) {
            log.debug("睡 2 小时");
            Thread.sleep(2000);
        }
    }

    public void study() throws InterruptedException {
        synchronized (studyRoom) {
            log.debug("学 1 小时");
            Thread.sleep(1000);
        }
    }
}
18:39:46.729 [小明] DEBUG juc.lock.BigRoom - 学 1 小时
18:39:46.729 [小红] DEBUG juc.lock.BigRoom - 睡 2 小时

将锁的粒度细化:

  • 好处:可以增强并发度
  • 坏处:如果一个线程需要同时获得多把锁,容易发生死锁

二、死锁

一个线程需要同时获取多把锁,这时可能发生死锁:

  • t1 线程 获得 A对象 锁,接下来想获取 B对象 的锁
  • t2 线程 获得 B对象 锁,接下来想获取 A对象 的锁
@Slf4j
public class DeadLock {

    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");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B) {
                    log.debug("lock B");
                    log.debug("操作...");
                }
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            synchronized (B) {
                log.debug("lock B");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (A) {
                    log.debug("lock A");
                    log.debug("操作...");
                }
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}
19:06:26.007 [t2] DEBUG juc.lock.DeadLock - lock B
19:06:26.007 [t1] DEBUG juc.lock.DeadLock - lock A

定位死锁

  • 使用 jconsole工具
  • 使用 jps 定位进程 id,再用 jstack 定位死锁
hongcaixia@hongcaixiadeMacBook-Pro thread % jps
4146 Launcher
4147 DeadLock
46231 
30200 TestProducerConsumer
46764 Launcher
5070 Jps
hongcaixia@hongcaixiadeMacBook-Pro thread % jstack 4147
"t2" #12 prio=5 os_prio=31 tid=0x00007fe0a7882800 nid=0x7e03 waiting for monitor entry [0x00007000062ef000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at juc.lock.DeadLock.lambda$test1$1(DeadLock.java:47)
        - waiting to lock <0x0000000716059778> (a java.lang.Object)
        - locked <0x0000000716059788> (a java.lang.Object)
        at juc.lock.DeadLock$$Lambda$2/1212899836.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"t1" #11 prio=5 os_prio=31 tid=0x00007fe0b007c800 nid=0x5703 waiting for monitor entry [0x00007000061ec000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at juc.lock.DeadLock.lambda$test1$0(DeadLock.java:32)
        - waiting to lock <0x0000000716059788> (a java.lang.Object)
        - locked <0x0000000716059778> (a java.lang.Object)
        at juc.lock.DeadLock$$Lambda$1/997608398.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x00007fe0a700bcd8 (object 0x0000000716059778, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x00007fe0a700df38 (object 0x0000000716059788, a java.lang.Object),
  which is held by "t2"

哲学家就餐问题

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

public class PhilosopherMeal {

    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
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();
                }
            }
        }
    }

    Random random = new Random();
    private void eat(){
        log.debug("eating...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 筷子类
 */
class Chopstick {
    String name;

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

    @Override
    public String toString() {
        return "筷子{" + name + '}';
    }
}
19:21:52.361 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:52.361 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:21:53.368 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:53.369 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:21:54.371 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:54.371 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:21:55.372 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:56.376 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:57.376 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:58.379 [亚里士多德] DEBUG juc.lock.Philosopher - eating...

检查死锁:

hongcaixia@hongcaixiadeMacBook-Pro thread % jps
46231 
30200 TestProducerConsumer
11948 Launcher
46764 Launcher
11949 PhilosopherMeal
13101 Jps
hongcaixia@hongcaixiadeMacBook-Pro thread % jstack 11949
Found one Java-level deadlock:
=============================
"阿基米德":
  waiting to lock monitor 0x00007fbc430cf8b8 (object 0x00000007156f92b0, a juc.lock.Chopstick),
  which is held by "苏格拉底"
"苏格拉底":
  waiting to lock monitor 0x00007fbc430ccc08 (object 0x00000007156f92f0, a juc.lock.Chopstick),
  which is held by "柏拉图"
"柏拉图":
  waiting to lock monitor 0x00007fbc430ccb58 (object 0x00000007156f9330, a juc.lock.Chopstick),
  which is held by "亚里士多德"
"亚里士多德":
  waiting to lock monitor 0x00007fbc430cf808 (object 0x00000007156f9370, a juc.lock.Chopstick),
  which is held by "赫拉克利特"
"赫拉克利特":
  waiting to lock monitor 0x00007fbc430cf758 (object 0x00000007156f93b0, a juc.lock.Chopstick),
  which is held by "阿基米德"

Java stack information for the threads listed above:
===================================================
"阿基米德":
        at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
        - waiting to lock <0x00000007156f92b0> (a juc.lock.Chopstick)
        - locked <0x00000007156f93b0> (a juc.lock.Chopstick)
"苏格拉底":
        at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
        - waiting to lock <0x00000007156f92f0> (a juc.lock.Chopstick)
        - locked <0x00000007156f92b0> (a juc.lock.Chopstick)
"柏拉图":
        at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
        - waiting to lock <0x00000007156f9330> (a juc.lock.Chopstick)
        - locked <0x00000007156f92f0> (a juc.lock.Chopstick)
"亚里士��德":
        at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
        - waiting to lock <0x00000007156f9370> (a juc.lock.Chopstick)
        - locked <0x00000007156f9330> (a juc.lock.Chopstick)
"赫拉克利特":
        at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
        - waiting to lock <0x00000007156f93b0> (a juc.lock.Chopstick)
        - locked <0x00000007156f9370> (a juc.lock.Chopstick)

Found 1 deadlock.

避免死锁

顺序加锁,每个线程都按照相同的顺序拿锁
解决哲学家就餐问题:让他们按照顺序拿锁:

    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("阿基米德", c1, c5).start();
    }

如果由于某个线程进入了死循环,导致其它线程一直等待,对于这种情况 linux 下可以通过 top 先定位到CPU 占用高的 Java 进程,再利用 top -Hp 进程id 来定位是哪个线程,最后再用 jstack 排查

三、活锁

两个线程互相改变对方的结束条件,最后谁也无法结束

@Slf4j
public class LiveLock {
    static volatile int count = 10;

    public static void main(String[] args) {
        new Thread(() -> {
            // 期望减到 0 退出循环
            while (count > 0) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                log.debug("count: {}", count);
            }
        }, "t1").start();
        new Thread(() -> {
            // 期望超过 20 退出循环
            while (count < 20) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count++;
                log.debug("count: {}", count);
            }
        }, "t2").start();
    }
}
19:32:44.468 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:44.468 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:44.675 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:44.675 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:44.877 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:44.877 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:45.082 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:45.082 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:45.286 [t1] DEBUG juc.lock.LiveLock - count: 8
19:32:45.286 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:45.490 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:45.490 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:45.693 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:45.693 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:45.896 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:45.896 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.098 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:46.098 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.301 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:46.301 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.504 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.504 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:46.706 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:46.706 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:46.908 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:46.908 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:47.113 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:47.113 [t1] DEBUG juc.lock.LiveLock - count: 8
19:32:47.318 [t2] DEBUG juc.lock.LiveLock - count: 10
···

解决:让两个线程运行时间错开,增加随机的睡眠时间

四、饥饿

一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束。
针对哲学家就餐问题使用顺序拿锁,其中大部分时间都是赫拉克利特和苏格拉底拿到锁,而阿基米德一直没机会拿到锁,没机会执行。

19:43:41.832 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:42.836 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:42.836 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:43.837 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:43.837 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:44.838 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:44.838 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:45.838 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:45.838 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:46.839 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:46.839 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:47.839 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:47.839 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:48.839 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:48.839 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:49.841 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:49.841 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:50.844 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:50.844 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:51.846 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:51.846 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:52.847 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:52.847 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:53.847 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:53.847 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:54.852 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:54.852 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:55.857 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:55.857 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:56.860 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:56.860 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:57.864 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:57.864 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:58.869 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:58.869 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:59.875 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:59.875 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:00.875 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:00.875 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:01.880 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:01.880 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:02.883 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:02.883 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:03.886 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:03.886 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:04.886 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:04.886 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:05.890 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:06.891 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:07.897 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:08.897 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:09.901 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:10.905 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:11.907 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:12.912 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:13.917 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:14.919 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:15.922 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:16.924 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:17.928 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:18.931 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:19.934 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
···
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容