美文网首页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并发

    1.并发编程中的锁 并发编程中的各种锁java高并发锁的3种实现Java并发机制及锁的实现原理 2.线程池核心线程...

  • Java并发编程 活锁

    1. 什么是活锁 活锁是另一个并发问题,它和死锁很相似。在活锁中,两个或多个线程彼此间一直在转移状态,而不像我们上...

  • Java并发编程 - 共享锁

    Java并发编程 - 深入剖析ReentrantLock之非公平锁加锁流程(第1篇)Java并发编程 - 深入剖析...

  • Java并发编程 - 深入剖析Condition

    Java并发编程 - 深入剖析ReentrantLock之非公平锁加锁流程(第1篇)Java并发编程 - 深入剖析...

  • Java并发学习笔记 -- Java中的Lock、volatil

    Java并发 参考资料:《Java并发编程的艺术》并发番@Synchronized一文通(1.8版) 一、锁 1....

  • Java并发编程

    一、Java并发编程的挑战 1.1 如何减少上下文切换? 无锁并发编程: CAS算法:Java的Atomic包使用...

  • java并发专题

    任何一个java工程师都绕不开java并发编程,但是java并发编程涉及的东西还是很多。其中包括各种锁、线程池、线...

  • 详解Java中的锁

    并发编程,尤其是Java编程中经常涉及到锁的概念。我们听过乐观锁/悲观锁、公平锁/非公平锁、独享锁/共享锁、自旋锁...

  • 死锁-活锁

    死锁大家都知道,但是 有没有老铁 知道活锁呢?我在看《并发编程实战》的时候 了解到这个名次 活锁 活锁 是指 活锁...

  • 这才是Redis分布式锁的正确姿势!你掌握了吗?

    前言 在Java中,我们对于锁会比较熟悉,常用的有 synchronized、Lock锁,在java并发编程中,我...

网友评论

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

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