美文网首页Java-多线程
Java并发编程 活锁

Java并发编程 活锁

作者: 香沙小熊 | 来源:发表于2020-12-28 16:16 被阅读0次

    1. 什么是活锁

    活锁是另一个并发问题,它和死锁很相似。在活锁中,两个或多个线程彼此间一直在转移状态,而不像我们上个例子中互相等待。结果就是所有线程都不能执行它们各自的任务。

    虽然线程并没有阻塞,也始终在运行(所以叫做“活锁”,线程时“活”的),但是程序却得不到进展,因为线程始终重复做同样的事

    形象比喻
    活锁:马路中间有条小桥,只能容纳一辆车经过,桥两头开来两辆车A和B,A比较礼貌,示意B先过,B也比较礼貌,示意A先过,结果两人一直谦让谁也过不去。

    2. 演示活锁

    2.1. 相互协作的线程彼此响应从而修改自己状态,导致无法执行下去。

    比如两个很有礼貌的人在同一条路上相遇,彼此给对方让路,但是又在同一条路上遇到了。互相之间反复的避让下去

    public class LiveLock {
    
        static class Spoon {
    
            private Diner owner;
    
            public Spoon(Diner owner) {
                this.owner = owner;
            }
    
            public Diner getOwner() {
                return owner;
            }
    
            public void setOwner(Diner owner) {
                this.owner = owner;
            }
    
            public synchronized void use() {
                System.out.printf("%s吃完了!", owner.name);
    
    
            }
        }
    
        static class Diner {
    
            private String name;
            private boolean isHungry;
    
            public Diner(String name) {
                this.name = name;
                isHungry = true;
            }
    
            public void eatWith(Spoon spoon, Diner spouse) {
                while (isHungry) {
                    if (spoon.owner != this) {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
    
                    if (spouse.isHungry ) {
                        System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
                        spoon.setOwner(spouse);
                        continue;
                    }
    
                    spoon.use();
                    isHungry = false;
                    System.out.println(name + ": 我吃完了");
                    spoon.setOwner(spouse);
    
                }
            }
        }
    
    
        public static void main(String[] args) {
            Diner husband = new Diner("老公");
            Diner wife = new Diner("老婆");
    
            Spoon spoon = new Spoon(husband);
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    husband.eatWith(spoon, wife);
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    wife.eatWith(spoon, husband);
                }
            }).start();
        }
    }
    
    
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    老公: 亲爱的老婆你先吃吧
    老婆: 亲爱的老公你先吃吧
    ...省略
    
    2.2 消息队列发生异常重试策略

    消息队列当发生异常的时候,消息消费者回滚事务并把消息放到队列头中,然后相同的消息又从队列头中被读到,又会造成异常并再次放入到队列头中。如此循坏往复,消费者永远读不到队列中其他的消息。

    3.如何解决活锁问题

    1. 选择一个随机退让,使得具备一定的随机性
    public class LiveLockFix {
    
        static class Spoon {
    
            private Diner owner;
    
            public Spoon(Diner owner) {
                this.owner = owner;
            }
    
            public Diner getOwner() {
                return owner;
            }
    
            public void setOwner(Diner owner) {
                this.owner = owner;
            }
    
            public synchronized void use() {
                System.out.printf("%s吃完了!", owner.name);
    
    
            }
        }
    
        static class Diner {
    
            private String name;
            private boolean isHungry;
    
            public Diner(String name) {
                this.name = name;
                isHungry = true;
            }
    
            public void eatWith(Spoon spoon, Diner spouse) {
                while (isHungry) {
                    if (spoon.owner != this) {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
                    Random random = new Random();
                    if (spouse.isHungry && random.nextInt(10) < 9) {
                        System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
                        spoon.setOwner(spouse);
                        continue;
                    }
    
                    spoon.use();
                    isHungry = false;
                    System.out.println(name + ": 我吃完了");
                    spoon.setOwner(spouse);
    
                }
            }
        }
    
    
        public static void main(String[] args) {
            Diner husband = new Diner("老公");
            Diner wife = new Diner("老婆");
    
            Spoon spoon = new Spoon(husband);
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    husband.eatWith(spoon, wife);
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    wife.eatWith(spoon, husband);
                }
            }).start();
        }
    }
    
    image.png
    2. 消息队列重试策略修复
    • 重试次数限定
    • 将不可修复的错误不要重试(放在死信队列或者数据中,跑脚本或人工解决)
    • 将消息放到队列尾部

    特别感谢:

    悟空

    相关文章

      网友评论

        本文标题:Java并发编程 活锁

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