2011-05-06 2 views
2

'작은 책 세마포어'에서 'Exclusive Queue'문제에 대한 해결책을 쓰려고합니다. 문제는 다음과 같이 표시됩니다.Java Concurrency : Exclusive Queue 문제

스레드는 볼룸 댄서를 대표하며 2 가지 종류의 댄서, 리더 및 추종자가 댄스 플로어에 들어가기 전에 대기열에서 대기한다고 가정합니다. 리더가 도착하면 기다리는 추종자가 있는지 확인합니다. 그렇다면 둘 다 진행할 수 있습니다. 그렇지 않으면 기다립니다. 마찬가지로 팔로워가 도착하면 리더를 확인하고 이에 따라 수익을 얻거나 기다립니다. 또한, 각 리더는 한 명의 팔로어와 동시에 댄스를 불러올 수 있으며 그 반대도 마찬가지입니다.

책에는 세마포어를 사용하는 해결책이 언급되어 있지만 자바에서 객체 잠금을 사용하여 해결하려고합니다.

ExclusiveQueuePrimitive.java :

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 

public class ExclusiveQueuePrimitive { 

    public static void main(String[] args) throws InterruptedException { 
     System.out 
       .println("-------------------------------Application START-------------------"); 
     final int NUM_RUN = 1000; 
     // for (int j=0; j<NUM_RUN; j++) { 
     for (;;) { 
      Counters c = new Counters(); 
      int NUM_THREADS = 5; 

      List<Thread> threads = new ArrayList<Thread>(); 

      for (int i = 0; i < NUM_THREADS; i++) { 
       Thread tl = new Thread(new Leader(c, i + 1)); 
       Thread tf = new Thread(new Follower(c, i + 1)); 
       threads.add(tf); 
       threads.add(tl); 
       tf.start(); 
       tl.start(); 
      } 
      for (int i = 0; i < threads.size(); i++) { 
       Thread t = threads.get(i); 
       t.join(); 
      } 
     } 
     // System.out.println("--------------------------------Application END-------------------"); 
    } 
} 

class Counters { 

    public int leaders = 0; 
    public int followers = 0; 
    //public final Lock countMutex = new ReentrantLock(); 

    public boolean printed = false; 
    public Lock printLock = new ReentrantLock(); 



    public final Lock leaderQueue = new ReentrantLock(); 
    public final Lock followerQueue = new ReentrantLock(); 

    public void dance(String str) { 
     System.out.println("" + str); 
    } 

    public void printLine() { 
     System.out.println(""); 
    } 
} 

class Leader implements Runnable { 

    final Counters c; 
    final int num; 

    public Leader(Counters counters, int num) { 
     this.c = counters; 
     this.num = num; 
    } 

    @Override 
    public void run() { 

     synchronized (c.leaderQueue) { 
      try { 
       if (c.followers > 0) { 

         c.followers--; 
         synchronized (c.followerQueue) { 
          c.followerQueue.notify(); 
         } 


       } else { 
        c.leaders++; 

        c.leaderQueue.wait(); 
       } 
       c.dance("Leader " + num + " called dance"); 
      } catch (InterruptedException e) { 

       e.printStackTrace(); 
      } 

     } 
    } 
} 

class Follower implements Runnable { 

    final Counters c; 
    final int num; 

    public Follower(Counters counters, int num) { 
     this.c = counters; 
     this.num = num; 
    } 

    @Override 
    public void run() { 

     synchronized (c.followerQueue) { 
      try { 
       if (c.leaders > 0) { 
        synchronized (c.leaderQueue) { 
         c.leaders--; 
         c.leaderQueue.notify(); 
        } 
       } else { 
        c.followers++; 
        c.followerQueue.wait(); 
       } 
       c.dance("Follower " + num + " called dance"); 

      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

     } 
    } 
} 

그러나, 잠시 동안 실행 한 후,이 끊 여기 내 솔루션입니다. 교착 상태가 어디인지 어떻게 해결할 수 있는지 말해 줄 수 있어요. 또한 리더와 추종자가 한 쌍이 된 후에도 새로운 라인을 인쇄하고 싶습니다. 어떻게해야합니까?

+0

마치 동일한 개체에서 동기화하지 않는 것처럼 보이지만 문제가되지 않습니까? –

답변

0

c.followerQueue에 뮤텍스가 있고 c.leaderQueue에 뮤텍스가 있습니다. 한 쪽에서는 리더 대기열을 먼저 가져온 다음 팔로어 대기열을 가져오고 다른 쪽에서는 먼저 팔로워 대기열을 가져옵니다.

이것은 좋지 않습니다. 한 쪽이 추종자 자물쇠를 잡고 다른 쪽이 리더 자물쇠를 잡으면 아무도 진행할 수 없습니다. 잠금 획득에 대한 일관성없는 주문을 피하십시오.

각 쌍이 끝난 후 한 줄을 인쇄하려면 지시선이나 추종자 중 하나만 인쇄하면되지만 둘 다 인쇄되지는 않습니다. 리더 마무리에 대한 코드는 ... 추종자도 완료 의미

고전적인 교착 상태입니다
+0

세 번째 개체를 잠금으로 사용하는 경우 동기화 된 블록은 if, else 블록을 처리하므로 방법을 알 수 없습니다. 그러나 거기에 다른 중 wait() 있습니다. – sachin

2

:

class Leader { 
    synchronized (c.leaderQueue) { ... 
     synchronized (c.followerQueue) { ... } 
    } 
} 

class Follower { 
    synchronized (c.followerQueue) { ... 
     synchronized (c.leaderQueue) { ... } 
    } 
} 

것을 방지하기 위해 가장 간단한 것은 같은 순서로 잠금을 잡아하는 것입니다 (BTW 사용 Locksynchronized은 함께 사용하지 않는 것이 좋습니다. 교착 상태를 감지하는 다른 기술이 있지만 작업 컨텍스트에서 알고리즘을 변경하는 것이 더 유리합니다.

간단한 시작 - 로직을 수정하려면 단일 잠금을 사용하고, 정확성을 손상시키지 않으면 서 더 많은 현명한 작업을 수행하여 동시성을 향상시킵니다.

+0

하지만 여전히, 우리가 실제로 어떻게 해결할 수 있는지에 대한 단서가 있습니까? – sachin

+0

@sachin 두 가지가 있습니다 : 1) 두 대기열에 동일한 잠금을 사용합니다. 2) 'Leader'와 'Follower'에 동일한 잠금 순서를 사용합니다. 3) 잠금을 제거하고 LinkedBlockingQueue를 사용하여 달성하다 –