라이브 록의 좋은 예?


141

라이브 록이 무엇인지 이해하지만 코드 기반의 좋은 예가 누구인지 궁금합니다. 그리고 코드 기반으로, "두 사람이 복도에서 서로를 지나치려고한다" 는 의미 는 아닙니다 . 다시 읽으면 점심을 잃을 것입니다.


96
복도에서 서로를 지나치려고하는 두 사람의 소프트웨어 시뮬레이션은 어떻습니까?
1800 정보

36
저주 해! 나는 점심을 잃었다!
Alex Miller

3
이상하게 적절 : seuss.wikia.com/wiki/The_Zax
NotMe

호기심에 빠진
Jorjon

4
: 복도에서 서로를지나려고 두 사람 gist.github.com/deepankarb/d2dd6f21bc49902376e614d3746b8965 P :
아이스 맨

답변:


119

여기 남편과 아내가 수프를 먹으려 고하지만 그 사이에 숟가락이 하나있는 라이브 락의 매우 간단한 자바 예제가 있습니다. 각 배우자는 너무 공손하며, 다른 배우자가 아직 먹지 않은 경우 숟가락을 통과합니다.

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방법뿐만 아니라 동기화 할 수 있나요? 유효 Java에서 " 읽기 및 쓰기 " 둘 다를 제외하고 " 동기화는 효과가 없습니다 .
이상현

그는 사용하지 말아야 Thread.join()보다는 Thread.sleep()그가 식사를 할 수있는 배우자 대기하고 싶어하기 때문에?
Solace

이 특정 예에서 라이브 록 문제를 극복하려면 어떻게해야합니까?
Thor

1
getOwner메소드는 동기화되어 있어도 setOwner스레드를 사용하는 getOwner(또는 필드에 owner직접 액세스하는 ) 스레드가 다른 스레드가 수행 한 변경 사항을 보도록 보장하지 않으므로 동기화 되어야합니다 setOwner. 이 vid는 이것을 매우 신중하게 설명합니다. youtube.com/watch?v=WTVooKLLVT8
Timofey

2
읽기 및 쓰기는 참조 변수에 대한 원자적인 작업이므로 method에 synchronized 키워드 를 사용할 필요가 없습니다 setOwner.
atiqkhaled.

75

Flippant 의견은 제쳐두고, 교착 상태 상황을 감지하고 처리하려고 시도하는 코드가 있습니다. 두 스레드가 교착 상태를 감지하고 서로 "비켜"려고하면주의를 기울이지 않으면 서 항상 "비켜서"루프에 멈춰서 앞으로 나아갈 수 없게됩니다.

"제쳐두고"라는 말은 그들이 자물쇠를 풀고 다른 사람이 자물쇠를 얻도록 시도한다는 것을 의미합니다. 이 작업을 수행하는 두 개의 스레드가있는 상황 (의사 코드)을 상상할 수 있습니다.

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

경쟁 조건을 제쳐두고, 우리가 여기있는 것은 두 스레드가 동시에 들어가면 내부 루프에서 계속 진행되지 않는 상황입니다. 분명히 이것은 간단한 예입니다. 이 문제를 해결하는 방법은 스레드가 기다리는 시간에 임의의 종류를 무작위로 넣는 것입니다.

올바른 수정은 항상 잠금 계층 구조를 존중하는 것 입니다. 자물쇠를 얻는 순서를 고르고 그에 충실하십시오. 예를 들어, 두 스레드가 항상 lock2 전에 lock1을 획득하면 교착 상태가 발생할 가능성이 없습니다.


네, 이해합니다 실제 코드 예제를 찾고 있습니다. 문제는 "제쳐두고"는 무엇을 의미하며 그러한 시나리오를 어떻게 생성 하는가입니다.
Alex Miller

이것이 좋은 예라고 생각하지만 이것이 라이브 록으로 이어질 가능성이 있습니까? 스레드가 큰 소리로 실행되는 시간과 예약 된 시간의 불일치로 인해 하나의 함수가 둘 다 잡을 수있는 창이 열리게 될 가능성이 훨씬 더 높지 않습니까?
DubiousPusher

비록 그것이 확실하게 빠져 나올 것이기 때문에 안정적인 라이브 록은 아니지만, 나는 그것이 충분히 설명에 적합하다고 생각합니다.
1800 INFORMATION

훌륭하고 의미있는 예.
alecov

7

허용 된 답변으로 표시된 답변이 없으므로 라이브 잠금 예제를 만들려고 시도했습니다.

오리지널 프로그램 은 2012 년 4 월에 다양한 멀티 스레딩 개념을 배우기 위해 작성되었습니다. 이번에는 교착 상태, 경쟁 조건, 라이브 록 등을 생성하도록 수정했습니다.

먼저 문제 진술을 이해합시다.

쿠키 메이커 문제

ChocoPowederContainer , WheatPowderContainer 성분 용기가 있습니다 . CookieMaker 는 성분 용기에서 일정량의 분말을 취해 쿠키 를 굽습니다 . 쿠키 메이커가 컨테이너를 비우면 다른 컨테이너를 검사하여 시간을 절약합니다. 필러 가 필요한 컨테이너를 채울 때까지 기다립니다 . 있다 필러 일정한 간격에 컨테이너를 확인하고 용기를 필요로하는 경우 일부 수량을 채 웁니다.

github 에서 전체 코드를 확인하십시오 .

간단하게 구현을 설명하겠습니다.

  • 필러 를 데몬 스레드로 시작 합니다. 따라서 일정한 간격으로 컨테이너를 계속 채 웁니다. 컨테이너를 먼저 채우려면 컨테이너를 잠그고-> 파우더가 필요한지 확인-> 채우기-> 컨테이너를 기다리는 모든 제조사에게 신호를 보냅니다-> 컨테이너 잠금을 해제하십시오.
  • CookieMaker를 만들고 최대 8 개의 쿠키를 동시에 구울 수 있도록 설정했습니다. 쿠키를 만들기 위해 8 개의 스레드를 시작합니다.
  • 각 메이커 스레드는 2 개의 호출 가능한 서브 스레드를 만들어 컨테이너에서 파우더를 가져옵니다.
  • 서브 스레드는 컨테이너를 잠그고 파우더가 충분한 지 확인하십시오. 그렇지 않은 경우 잠시 기다립니다. 필러가 컨테이너를 채우면 파우더를 가져 와서 컨테이너를 잠금 해제합니다.
  • 이제는 혼합물 만들기 및 베이킹과 같은 다른 활동을 완료합니다.

코드를 살펴 보자.

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

필러 가 컨테이너를 채울 때까지 모든 것이 잘 작동합니다 . 그러나 필러를 시작하지 않거나 필러가 예기치 않은 휴가를 가면 하위 스레드의 상태가 계속 변경되어 다른 제조업체가 컨테이너를 검사하고 확인할 수 있습니다.

스레드 상태 및 교착 상태를 감시 하는 데몬 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 주제에 추가 된 메시지 (예 : 단일 객체 그래프의 업데이트를 두 번 이상 저장)에 응답하여 유사한 성능 특성을 가진 별도의 시스템에서 발생하며 제어 할 수없는 것을 보았습니다. 잠금 순서.

경우 좋은 해결책 은 경쟁하는 소비자를 보유하는 것입니다 (관련되지 않은 객체에서 작업을 분할하여 가능한 한 체인에서 중복 처리를 방지하십시오).

덜 바람직한 (ok, dirty-hack) 솔루션은 타이밍 불운 (처리의 힘 차이의 종류)을 미리 깨뜨 리거나 다른 알고리즘이나 임의의 요소를 사용하여 교착 상태 후에 중단하는 것입니다. 잠금 처리 순서가 각 프로세스에 대해 "고정적"일 수 있기 때문에 여전히 문제가있을 수 있으며, 대기 재 시도에서 고려되지 않는 특정 시간이 소요됩니다.

또 다른 솔루션 (적어도 SQL Server의 경우)은 다른 격리 수준 (예 : 스냅 샷)을 시도하는 것입니다.


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라이브 록이 없습니다.
JaviMerino

0

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

버그 : use ()에서 print는 사용되지 않으며 배고픈 플래그는 False로 설정되지 않습니다.
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.