代码之家  ›  专栏  ›  技术社区  ›  Alex Miller

活锁的好例子?

  •  125
  • Alex Miller  · 技术社区  · 15 年前

    我知道活锁是什么,但我想知道有没有人有一个很好的代码例子呢?基于代码,我知道 意思是“两个人在走廊里试图超越对方”。如果我再看一遍,我的午餐就没了。

    10 回复  |  直到 8 年前
        1
  •  109
  •   Jeremy Elbourn    9 年前

    这是一个非常简单的Java活灵活现的例子,丈夫和妻子在尝试喝汤,但是他们之间只有一个勺子。夫妻双方都太客气了,如果对方还没吃东西,他们会把勺子递给对方。

    public class Livelock {
        static class Spoon {
            private Diner owner;
            public Spoon(Diner d) { owner = d; }
            public Diner getOwner() { return owner; }
            public synchronized void setOwner(Diner d) { owner = d; }
            public synchronized void use() { 
                System.out.printf("%s has eaten!", owner.name); 
            }
        }
    
        static class Diner {
            private String name;
            private boolean isHungry;
    
            public Diner(String n) { name = n; isHungry = true; }       
            public String getName() { return name; }
            public boolean isHungry() { return isHungry; }
    
            public void eatWith(Spoon spoon, Diner spouse) {
                while (isHungry) {
                    // Don't have the spoon, so wait patiently for spouse.
                    if (spoon.owner != this) {
                        try { Thread.sleep(1); } 
                        catch(InterruptedException e) { continue; }
                        continue;
                    }                       
    
                    // If spouse is hungry, insist upon passing the spoon.
                    if (spouse.isHungry()) {                    
                        System.out.printf(
                            "%s: You eat first my darling %s!%n", 
                            name, spouse.getName());
                        spoon.setOwner(spouse);
                        continue;
                    }
    
                    // Spouse wasn't hungry, so finally eat
                    spoon.use();
                    isHungry = false;               
                    System.out.printf(
                        "%s: I am stuffed, my darling %s!%n", 
                        name, spouse.getName());                
                    spoon.setOwner(spouse);
                }
            }
        }
    
        public static void main(String[] args) {
            final Diner husband = new Diner("Bob");
            final Diner wife = new Diner("Alice");
    
            final Spoon s = new Spoon(husband);
    
            new Thread(new Runnable() { 
                public void run() { husband.eatWith(s, wife); }   
            }).start();
    
            new Thread(new Runnable() { 
                public void run() { wife.eatWith(s, husband); } 
            }).start();
        }
    }
    
        2
  •  71
  •   Mikhail Vladimirov    12 年前

    撇开轻率的注释不谈,我们知道出现的一个例子是代码,它试图检测和处理死锁情况。如果两个线程检测到一个死锁,并试图为对方“让开”,而不加注意,它们最终将陷入一个始终“让开”且永远无法向前移动的循环中。

    我说的“让开”是指他们会释放锁,并试图让另一个获得它。我们可以想象两个线程这样做的情况(伪代码):

    // thread 1
    getLocks12(lock1, lock2)
    {
      lock1.lock();
      while (lock2.locked())
      {
        // attempt to step aside for the other thread
        lock1.unlock();
        wait();
        lock1.lock();
      }
      lock2.lock();
    }
    
    // thread 2
    getLocks21(lock1, lock2)
    {
      lock2.lock();
      while (lock1.locked())
      {
        // attempt to step aside for the other thread
        lock2.unlock();
        wait();
        lock2.lock();
      }
      lock1.lock();
    }
    

    撇开竞争条件不谈,我们这里的情况是,如果两个线程同时进入,那么它们最终将在内部循环中运行而不继续。显然这是一个简单的例子。一个简单的解决方法是在线程等待的时间内加入某种随机性。

    正确的解决方法是始终尊重 lock heirarchy . 选择一个你获得锁的顺序并坚持下去。例如,如果两个线程总是在lock2之前获取lock1,则不可能出现死锁。

        3
  •  6
  •   Amit Kumar Gupta    8 年前

    由于没有被标记为接受答案的答案,我尝试创建实时锁示例;

    Original program 我是在APR 2012编写的,学习了多线程的各种概念。这次我修改了它以创建死锁、竞争条件、livelock等。

    所以让我们先理解问题陈述;

    Cookie Maker问题

    有一些配料容器: 巧克力容器 , 小麦粉容器 . 厨具匠 从配料容器中取出一些粉末来烘烤 曲奇饼干 . 如果cookie生成器发现一个容器为空,它会检查另一个容器以节省时间。一直等到 填料 填充所需的容器。有一个 填料 他定期检查集装箱,如果集装箱需要,他会装一些。

    请检查上的完整代码 github ;

    让我简单地解释一下你的实现。

    • 我开始 填料 作为守护线程。所以它会定期给容器加满水。要先填充容器,它会锁定容器->检查是否需要粉末->填充->通知所有等待它的制造商->解锁容器。
    • 我创造 厨具匠 并设置它可以烘烤多达8个饼干并行。我开始用8根线烤饼干。
    • 每个制造线程创建2个可调用的子线程,以从容器中获取粉末。
    • 子线程在容器上锁定并检查它是否有足够的粉末。如果没有,等一段时间。一旦填充物填满容器,它就会带走粉末,并打开容器。
    • 现在它完成了其他活动,如:制作混合物和烘烤等。

    让我们看看代码:

    厨师.java

    private Integer getMaterial(final Ingredient ingredient) throws Exception{
            :
            container.lock();
            while (!container.getIngredient(quantity)) {
                container.empty.await(1000, TimeUnit.MILLISECONDS);
                //Thread.sleep(500); //For deadlock
            }
            container.unlock();
            :
    }
    

    ingredientcontainer.java

    public boolean getIngredient(int n) throws Exception {
        :
        lock();
        if (quantityHeld >= n) {
            TimeUnit.SECONDS.sleep(2);
            quantityHeld -= n;
            unlock();
            return true;
        }
        unlock();
        return false;
    }
    

    一切顺利,直到 填料 正在装满容器。但如果我忘记启动填充程序,或者填充程序意外离开,子线程会不断更改其状态,以允许其他制造商去检查容器。

    我还创建了一个守护进程 ThreadTracer 它监视线程状态和死锁。这是控制台的输出;

    2016-09-12 21:31:45.065 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:RUNNABLE, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:TIMED_WAITING]
    2016-09-12 21:31:45.065 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:TIMED_WAITING, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:TIMED_WAITING]
    WheatPowder Container has 0 only.
    2016-09-12 21:31:45.082 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:TIMED_WAITING, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:RUNNABLE]
    2016-09-12 21:31:45.082 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:TIMED_WAITING, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:TIMED_WAITING]
    

    你会注意到子线程并改变它们的状态和等待。

        4
  •  4
  •   Kit    14 年前

    一个真实的(尽管没有确切的代码)示例是两个相互竞争的进程实时锁定,试图纠正SQL Server死锁,每个进程使用相同的等待重试算法重试。虽然这是计时的幸运,但我已经看到在具有类似性能特征的单独计算机上发生这种情况,以响应添加到ems主题的消息(例如,多次保存单个对象图的更新),并且无法控制锁顺序。

    很好的解决办法 这样的情况就是要有相互竞争的消费者(通过将工作划分到不相关的对象上,尽可能防止重复处理在链的最高层)。

    一个不太理想的(好的,肮脏的黑客)解决方案是提前打破计时坏运气(处理中的一种强制差异),或者在死锁后使用不同的算法或一些随机因素来打破它。这仍然可能有问题,因为对于每个进程来说,可能的锁获取顺序是“粘性的”,这需要一定的最短时间,而等待重试中没有考虑到这一点。

    另一个解决方案(至少对于sql server)是尝试不同的隔离级别(例如快照)。

        5
  •  2
  •   PoweredByRice    11 年前

    我把两个人在走廊里经过的例子编成代码。一旦意识到方向相同,这两条线就会相互避开。

    public class LiveLock {
        public static void main(String[] args) throws InterruptedException {
            Object left = new Object();
            Object right = new Object();
            Pedestrian one = new Pedestrian(left, right, 0); //one's left is one's left
            Pedestrian two = new Pedestrian(right, left, 1); //one's left is two's right, so have to swap order
            one.setOther(two);
            two.setOther(one);
            one.start();
            two.start();
        }
    }
    
    class Pedestrian extends Thread {
        private Object l;
        private Object r;
        private Pedestrian other;
        private Object current;
    
        Pedestrian (Object left, Object right, int firstDirection) {
            l = left;
            r = right;
            if (firstDirection==0) {
                current = l;
            }
            else {
                current = r;
            }
        }
    
        void setOther(Pedestrian otherP) {
            other = otherP;
        }
    
        Object getDirection() {
            return current;
        }
    
        Object getOppositeDirection() {
            if (current.equals(l)) {
                return r;
            }
            else {
                return l;
            }
        }
    
        void switchDirection() throws InterruptedException {
            Thread.sleep(100);
            current = getOppositeDirection();
            System.out.println(Thread.currentThread().getName() + " is stepping aside.");
        }
    
        public void run() {
            while (getDirection().equals(other.getDirection())) {
                try {
                    switchDirection();
                    Thread.sleep(100);
                } catch (InterruptedException e) {}
            }
        }
    } 
    
        6
  •  2
  •   ASOSTechNik    10 年前

    杰伯恩代码的C版:

    using System;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace LiveLockExample
    {
        static class Program
        {
            public static void Main(string[] args)
            {
                var husband = new Diner("Bob");
                var wife = new Diner("Alice");
    
                var s = new Spoon(husband);
    
                Task.WaitAll(
                    Task.Run(() => husband.EatWith(s, wife)),
                    Task.Run(() => wife.EatWith(s, husband))
                    );
            }
    
            public class Spoon
            {
                public Spoon(Diner diner)
                {
                    Owner = diner;
                }
    
    
                public Diner Owner { get; private set; }
    
                [MethodImpl(MethodImplOptions.Synchronized)]
                public void SetOwner(Diner d) { Owner = d; }
    
                [MethodImpl(MethodImplOptions.Synchronized)]
                public void Use()
                {
                    Console.WriteLine("{0} has eaten!", Owner.Name);
                }
            }
    
            public class Diner
            {
                public Diner(string n)
                {
                    Name = n;
                    IsHungry = true;
                }
    
                public string Name { get; private set; }
    
                private bool IsHungry { get; set; }
    
                public void EatWith(Spoon spoon, Diner spouse)
                {
                    while (IsHungry)
                    {
                        // Don't have the spoon, so wait patiently for spouse.
                        if (spoon.Owner != this)
                        {
                            try
                            {
                                Thread.Sleep(1);
                            }
                            catch (ThreadInterruptedException e)
                            {
                            }
    
                            continue;
                        }
    
                        // If spouse is hungry, insist upon passing the spoon.
                        if (spouse.IsHungry)
                        {
                            Console.WriteLine("{0}: You eat first my darling {1}!", Name, spouse.Name);
                            spoon.SetOwner(spouse);
                            continue;
                        }
    
                        // Spouse wasn't hungry, so finally eat
                        spoon.Use();
                        IsHungry = false;
                        Console.WriteLine("{0}: I am stuffed, my darling {1}!", Name, spouse.Name);
                        spoon.SetOwner(spouse);
                    }
                }
            }
        }
    }
    
        7
  •  1
  •   Alex Miller    15 年前

    这里的一个例子可能是使用定时锁来获得一个以上的锁,如果你不能全部获得,请退回并再试一次。

    boolean tryLockAll(Collection<Lock> locks) {
      boolean grabbedAllLocks = false;
      for(int i=0; i<locks.size(); i++) {
        Lock lock = locks.get(i);
        if(!lock.tryLock(5, TimeUnit.SECONDS)) {
          grabbedAllLocks = false;
    
          // undo the locks I already took in reverse order
          for(int j=i-1; j >= 0; j--) {
            lock.unlock();
          }
        }
      }
    }
    

    我可以想象这样的代码会有问题,因为有很多线程冲突并等待获取一组锁。但作为一个简单的例子,我不确定这是否很有说服力。

        8
  •  0
  •   nflacco    12 年前

    jelbourn代码的python版本:

    import threading
    import time
    lock = threading.Lock()
    
    class Spoon:
        def __init__(self, diner):
            self.owner = diner
    
        def setOwner(self, diner):
            with lock:
                self.owner = diner
    
        def use(self):
            with lock:
                "{0} has eaten".format(self.owner)
    
    class Diner:
        def __init__(self, name):
            self.name = name
            self.hungry = True
    
        def eatsWith(self, spoon, spouse):
            while(self.hungry):
                if self != spoon.owner:
                    time.sleep(1) # blocks thread, not process
                    continue
    
                if spouse.hungry:
                    print "{0}: you eat first, {1}".format(self.name, spouse.name)
                    spoon.setOwner(spouse)
                    continue
    
                # Spouse was not hungry, eat
                spoon.use()
                print "{0}: I'm stuffed, {1}".format(self.name, spouse.name)
                spoon.setOwner(spouse)
    
    def main():
        husband = Diner("Bob")
        wife = Diner("Alice")
        spoon = Spoon(husband)
    
        t0 = threading.Thread(target=husband.eatsWith, args=(spoon, wife))
        t1 = threading.Thread(target=wife.eatsWith, args=(spoon, husband))
        t0.start()
        t1.start()
        t0.join()
        t1.join()
    
    if __name__ == "__main__":
        main()
    
        9
  •  0
  •   Yi Zhang    8 年前

    我修改了@jelbourn的答案。 当他们中的一个发现另一个饿了,他(她)应该释放勺子并等待另一个通知,这样就发生了活锁。

    public class LiveLock {
        static class Spoon {
            Diner owner;
    
            public String getOwnerName() {
                return owner.getName();
            }
    
            public void setOwner(Diner diner) {
                this.owner = diner;
            }
    
            public Spoon(Diner diner) {
                this.owner = diner;
            }
    
            public void use() {
                System.out.println(owner.getName() + " use this spoon and finish eat.");
            }
        }
    
        static class Diner {
            public Diner(boolean isHungry, String name) {
                this.isHungry = isHungry;
                this.name = name;
            }
    
            private boolean isHungry;
            private String name;
    
    
            public String getName() {
                return name;
            }
    
            public void eatWith(Diner spouse, Spoon sharedSpoon) {
                try {
                    synchronized (sharedSpoon) {
                        while (isHungry) {
                            while (!sharedSpoon.getOwnerName().equals(name)) {
                                sharedSpoon.wait();
                                //System.out.println("sharedSpoon belongs to" + sharedSpoon.getOwnerName())
                            }
                            if (spouse.isHungry) {
                                System.out.println(spouse.getName() + "is hungry,I should give it to him(her).");
                                sharedSpoon.setOwner(spouse);
                                sharedSpoon.notifyAll();
                            } else {
                                sharedSpoon.use();
                                sharedSpoon.setOwner(spouse);
                                isHungry = false;
                            }
                            Thread.sleep(500);
                        }
                    }
                } catch (InterruptedException e) {
                    System.out.println(name + " is interrupted.");
                }
            }
        }
    
        public static void main(String[] args) {
            final Diner husband = new Diner(true, "husband");
            final Diner wife = new Diner(true, "wife");
            final Spoon sharedSpoon = new Spoon(wife);
    
            Thread h = new Thread() {
                @Override
                public void run() {
                    husband.eatWith(wife, sharedSpoon);
                }
            };
            h.start();
    
            Thread w = new Thread() {
                @Override
                public void run() {
                    wife.eatWith(husband, sharedSpoon);
                }
            };
            w.start();
    
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            h.interrupt();
            w.interrupt();
    
            try {
                h.join();
                w.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
        10
  •  0
  •   Athanasios V.    8 年前
    package concurrently.deadlock;
    
    import static java.lang.System.out;
    
    
    /* This is an example of livelock */
    public class Dinner {
    
        public static void main(String[] args) {
            Spoon spoon = new Spoon();
            Dish dish = new Dish();
    
            new Thread(new Husband(spoon, dish)).start();
            new Thread(new Wife(spoon, dish)).start();
        }
    }
    
    
    class Spoon {
        boolean isLocked;
    }
    
    class Dish {
        boolean isLocked;
    }
    
    class Husband implements Runnable {
    
        Spoon spoon;
        Dish dish;
    
        Husband(Spoon spoon, Dish dish) {
            this.spoon = spoon;
            this.dish = dish;
        }
    
        @Override
        public void run() {
    
            while (true) {
                synchronized (spoon) {
                    spoon.isLocked = true;
                    out.println("husband get spoon");
                    try { Thread.sleep(2000); } catch (InterruptedException e) {}
    
                    if (dish.isLocked == true) {
                        spoon.isLocked = false; // give away spoon
                        out.println("husband pass away spoon");
                        continue;
                    }
                    synchronized (dish) {
                        dish.isLocked = true;
                        out.println("Husband is eating!");
    
                    }
                    dish.isLocked = false;
                }
                spoon.isLocked = false;
            }
        }
    }
    
    class Wife implements Runnable {
    
        Spoon spoon;
        Dish dish;
    
        Wife(Spoon spoon, Dish dish) {
            this.spoon = spoon;
            this.dish = dish;
        }
    
        @Override
        public void run() {
            while (true) {
                synchronized (dish) {
                    dish.isLocked = true;
                    out.println("wife get dish");
                    try { Thread.sleep(2000); } catch (InterruptedException e) {}
    
                    if (spoon.isLocked == true) {
                        dish.isLocked = false; // give away dish
                        out.println("wife pass away dish");
                        continue;
                    }
                    synchronized (spoon) {
                        spoon.isLocked = true;
                        out.println("Wife is eating!");
    
                    }
                    spoon.isLocked = false;
                }
                dish.isLocked = false;
            }
        }
    }