作者: 我可能是个假开发 | 来源:发表于2023-12-11 09:27 被阅读0次

    一、锁细化

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

    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...
    ···
    

    相关文章

      网友评论

        本文标题:

        本文链接:https://www.haomeiwen.com/subject/kvkxgdtx.html