ตัวอย่างที่ดีของ livelock?


141

ฉันเข้าใจว่า livelock คืออะไร แต่ฉันสงสัยว่าใครมีตัวอย่างโค้ดที่ดี? และโดยใช้รหัสฉันไม่ได้หมายความว่า "คนสองคนพยายามที่จะผ่านกันในทางเดิน" ถ้าฉันอ่านมันอีกครั้งฉันจะทำอาหารเที่ยง


96
ลองจำลองซอฟต์แวร์ของคนสองคนที่พยายามจะผ่านกันในทางเดินได้อย่างไร
1800 ข้อมูล

36
สาปแช่งคุณ! ฉันทำอาหารกลางวันของฉันหาย!
Alex Miller

3
เหมาะสมอย่างยิ่ง: seuss.wikia.com/wiki/The_Zax
NotMe

เรื่องตลกที่เกี่ยวข้องสำหรับ fellas อยากรู้อยากเห็น: codingarchitect.wordpress.com/2006/01/18/…
Jorjon

4
คนสองคนพยายามที่จะผ่านกันในทางเดิน: gist.github.com/deepankarb/d2dd6f21bc49902376e614d3746b8965 : p
iceman

คำตอบ:


119

ต่อไปนี้เป็นตัวอย่าง Java ที่ง่ายมากของ livelock ที่สามีและภรรยาพยายามกินซุป แต่มีเพียงหนึ่งช้อนเท่านั้นระหว่างพวกเขา คู่สมรสแต่ละคนสุภาพเกินไปและจะผ่านช้อนถ้าคนอื่นยังไม่ได้กิน

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

6
ไม่getOwnerต้องทำการซิงโครไนซ์วิธีด้วยใช่ไหม จาก Effective Java "การซิงโครไนซ์จะไม่มีผลยกเว้นว่าทั้งการอ่านและการเขียน "
Sanghyun Lee

เขาไม่ควรใช้Thread.join()มากกว่าThread.sleep()เพราะเขาต้องการที่จะรอให้คู่สมรสของเขากิน?
Solace

เราควรทำอย่างไรเพื่อเอาชนะปัญหาของ livelock ในตัวอย่างนี้
ธ อร์

1
getOwnerวิธีการจะต้องตรงกันตั้งแต่แม้ว่าsetOwnerจะตรงนี้ไม่ได้รับประกันด้ายใช้getOwner(หรือการเข้าถึงสนามownerโดยตรง) จะเห็นการเปลี่ยนแปลงที่ทำโดยหัวข้ออื่น ๆ setOwnerที่มีประสิทธิภาพ Vid นี้อธิบายอย่างระมัดระวัง: youtube.com/watch?v=WTVooKLLVT8
Timofey

2
คุณไม่จำเป็นต้องใช้synchronized คำหลักสำหรับsetOwnerวิธีการเนื่องจากการอ่านและการเขียนเป็นการกระทำแบบอะตอมมิกสำหรับตัวแปรอ้างอิง
atiqkhaled

75

ความคิดเห็นที่ผิดพลาดตัวอย่างหนึ่งซึ่งเป็นที่ทราบกันดีอยู่ในโค้ดซึ่งพยายามตรวจจับและจัดการสถานการณ์การหยุดชะงัก หากสองเธรดตรวจพบการหยุดชะงักและพยายาม "หลีกเลี่ยง" ซึ่งกันและกันโดยไม่สนใจว่าจะจบลงด้วยการวนซ้ำเสมอ "ก้าวไปข้างหน้า" และไม่เคยจัดการย้ายไปข้างหน้า

โดย "หลีกทาง" ฉันหมายความว่าพวกเขาจะปลดล็อคและพยายามปล่อยให้อีกคนหนึ่งได้มา เราอาจจินตนาการถึงสถานการณ์ที่มีสองเธรดที่ทำสิ่งนี้ (pseudocode):

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

สภาพการแข่งขันกันสิ่งที่เรามีที่นี่คือสถานการณ์ที่ทั้งสองเธรดถ้าพวกเขาเข้ามาในเวลาเดียวกันจะจบลงด้วยการวิ่งในวงในโดยไม่ต้องดำเนินการต่อ เห็นได้ชัดว่านี่เป็นตัวอย่างที่ง่าย การแก้ไขแบบ naiive จะทำให้การสุ่มในเวลาที่เธรดรอ

การแก้ไขที่เหมาะสมอยู่เสมอเคารพลำดับชั้นล็อค เลือกคำสั่งที่คุณจะได้รับล็อคและทำตามนั้น ตัวอย่างเช่นถ้าทั้งสองเธรดได้รับ lock1 เสมอก่อนล็อก 2 ดังนั้นจึงไม่มีความเป็นไปได้ที่จะเกิดการหยุดชะงัก


ใช่ฉันเข้าใจแล้ว ฉันกำลังมองหาตัวอย่างรหัสจริงของสิ่งนั้น คำถามคือสิ่งที่ "ก้าวไปข้างหน้า" หมายถึงอะไรและมันสร้างสถานการณ์ดังกล่าว
Alex Miller

ฉันเข้าใจว่านี่เป็นตัวอย่างที่วางแผนไว้ แต่มีแนวโน้มว่าสิ่งนี้จะนำไปสู่การมีชีวิตชีวาหรือไม่? มันจะไม่เป็นไปได้หรือไม่ที่ในที่สุดหน้าต่างจะเปิดขึ้นที่ฟังก์ชันหนึ่งสามารถดึงทั้งสองได้เนื่องจากความไม่สอดคล้องกันในเวลาที่เธรดดังกล่าวทำงานและเมื่อมีการกำหนดเวลาไว้
DubiousPusher

แม้ว่ามันจะไม่ได้มีชีวิตชีวาเพราะเห็นได้ชัดว่าพวกมันจะแตกออกในที่สุด แต่ฉันคิดว่ามันเหมาะกับคำอธิบายที่ดีพอ
1800 ข้อมูล

ตัวอย่างที่ยอดเยี่ยมและมีความหมาย
alecov

7

เนื่องจากไม่มีคำตอบทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับได้ฉันพยายามสร้างตัวอย่างการล็อคสด

โปรแกรมต้นฉบับถูกเขียนโดยฉันในเดือนเมษายน 2012 เพื่อเรียนรู้แนวคิดที่หลากหลายของการมัลติเธรด ครั้งนี้ฉันได้แก้ไขมันเพื่อสร้างการหยุดชะงักสภาพการแข่งขัน livelock เป็นต้น

ดังนั้นมาทำความเข้าใจคำแถลงปัญหาก่อน

ปัญหาเครื่องทำคุกกี้

: มีบางภาชนะบรรจุส่วนผสมที่มีChocoPowederContainer , WheatPowderContainer CookieMakerต้องใช้ปริมาณของผงจากภาชนะบรรจุส่วนผสมการอบคุกกี้ หากผู้ผลิตคุกกี้พบภาชนะที่ว่างเปล่ามันจะตรวจสอบภาชนะอื่นเพื่อประหยัดเวลา และรอจนกว่าฟิลเลอร์เติมภาชนะที่ต้องการ มีความเป็นฟิลเลอร์ที่ตรวจสอบคอนเทนเนอร์ในช่วงเวลาปกติและเติมปริมาณบางอย่างถ้าต้องการคอนเทนเนอร์

กรุณาตรวจสอบรหัสที่สมบูรณ์เกี่ยวกับGitHub ;

ฉันขออธิบายการใช้งานของคุณโดยย่อ

  • ฉันเริ่มต้นฟิลเลอร์เป็น daemon ดังนั้นมันจะทำการบรรจุในช่วงเวลาปกติ ในการเติมตู้คอนเทนเนอร์ก่อนจะล็อคภาชนะ -> ตรวจสอบว่าจำเป็นต้องใช้ผง -> เติม -> ส่งสัญญาณผู้ผลิตทั้งหมดที่รอ -> ปลดล็อคภาชนะหรือไม่
  • ฉันสร้างCookieMakerและตั้งค่าว่ามันสามารถอบคุกกี้ได้ถึง 8 รายการพร้อมกัน และฉันเริ่มต้น 8 กระทู้เพื่ออบคุกกี้
  • แต่ละด้ายชงสร้าง 2 หัวข้อย่อย callable ที่จะใช้ผงจากภาชนะบรรจุ
  • sub-thread ใช้การล็อกบนคอนเทนเนอร์และตรวจสอบว่ามีผงเพียงพอหรือไม่ ถ้าไม่รอสักครู่ เมื่อฟิลเลอร์เติมภาชนะมันจะใช้ผงและปลดล็อคภาชนะ
  • ตอนนี้มันเสร็จสิ้นกิจกรรมอื่น ๆ เช่น: การผสมและการอบ ฯลฯ

ลองมาดูในรหัส:

CookieMaker.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;
}

ทุกอย่างทำงานได้ดีจนกระทั่งFillerเติมภาชนะ แต่ถ้าฉันลืมที่จะเริ่มเติมเพราะหรือฟิลเลอร์ไปลาที่ไม่คาดคิดหัวข้อย่อยจะเปลี่ยนสถานะของพวกเขาเพื่อให้ผู้ผลิตรายอื่นไปและตรวจสอบภาชนะ

ฉันยังสร้าง daemon 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

ตัวอย่างของจริง (แม้ว่าไม่มีรหัสที่แน่นอน) คือกระบวนการแข่งขันสองรายการที่ล็อคอยู่ในความพยายามที่จะแก้ไขการหยุดชะงักของเซิร์ฟเวอร์ SQL โดยแต่ละกระบวนการใช้อัลกอริทึมการรอการลองใหม่สำหรับการลองใหม่ ในขณะที่มันโชคดีของเวลาฉันได้เห็นสิ่งนี้เกิดขึ้นในเครื่องแยกที่มีคุณสมบัติคล้ายกันในการตอบสนองต่อข้อความที่เพิ่มในหัวข้อ EMS (เช่นบันทึกการอัพเดตกราฟวัตถุเดียวมากกว่าหนึ่งครั้ง) และไม่สามารถควบคุมได้ คำสั่งล็อค

ทางออกที่ดีในกรณีนี้คือการมีผู้บริโภคที่แข่งขันกัน (ป้องกันการประมวลผลซ้ำซ้อนในระดับสูงที่สุดเท่าที่จะเป็นไปได้โดยการแบ่งงานบนวัตถุที่ไม่เกี่ยวข้อง)

การแก้ปัญหาที่พึงประสงค์น้อยกว่า (ตกลงสกปรกแฮ็ค) คือการทำลายโชคร้ายเวลา (ชนิดของความแตกต่างของแรงในการประมวลผล) ล่วงหน้าหรือทำลายมันหลังจากการหยุดชะงักโดยใช้อัลกอริทึมที่แตกต่างกันหรือองค์ประกอบบางส่วนของการสุ่ม สิ่งนี้อาจยังมีปัญหาเนื่องจากอาจเป็นไปได้ว่าคำสั่งการล็อกนั้นเป็น "เหนียว" สำหรับแต่ละกระบวนการและใช้เวลาขั้นต่ำที่ไม่ได้คำนึงถึงในการรอการลองใหม่

อีกวิธีหนึ่ง (อย่างน้อยสำหรับ SQL Server) คือการลองระดับการแยกอื่น (เช่นภาพรวม)


2

ฉันเขียนโค้ดตัวอย่างของ 2 คนที่ผ่านในทางเดิน ทั้งสองเธรดจะหลีกเลี่ยงซึ่งกันและกันทันทีที่พวกเขารู้ทิศทางของพวกเขาเหมือนกัน

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) {}
        }
    }
} 

2

รหัส Jelbourn รุ่น 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);
                }
            }
        }
    }
}

1

ตัวอย่างหนึ่งที่นี่อาจใช้ tryLock ที่ตั้งเวลาเพื่อรับการล็อคมากกว่าหนึ่งครั้งและหากคุณไม่สามารถรับได้ทั้งหมดให้ปิดและลองอีกครั้ง

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

ฉันคิดว่ารหัสดังกล่าวอาจเป็นปัญหาได้เนื่องจากคุณมีเธรดจำนวนมากชนกันและรอรับชุดล็อค แต่ฉันไม่แน่ใจว่านี่เป็นตัวอย่างที่น่าสนใจสำหรับฉัน


1
สำหรับสิ่งนี้เพื่อให้มีชีวิตชีวาคุณจะต้องมีเธรดอื่นเพื่อรับการล็อกเหล่านั้นตามลำดับอื่น หากเธรดทั้งหมดใช้tryLockAll()กับการล็อกlocksตามลำดับเดียวกันจะไม่มี livelock
JaviMerino

0

ไพ ธ อนรหัสของ jelbourn:

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

ข้อบกพร่อง: ในการใช้งาน () การพิมพ์ไม่ได้ถูกใช้และที่สำคัญกว่านั้นคือธงหิวไม่ได้ตั้งค่าเป็นเท็จ
GDR

0

ฉันแก้ไขคำตอบของ @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();
        }
    }
}

0
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;
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.