0

제작자 - 소비자 모델에서 SynchronousQueue를 사용하는 테스트 예제를 작성했습니다. 그러나 그것은 잘 작동하지 않습니다. 다음은 내 코드는 다음과 같습니다생산자 - 소비자 모델에서 SynchronousQueue를 적절하게 사용하는 방법은 무엇입니까?

public class QueueTest { 

    String input; 
    int pos; 
    BlockingQueue<String> queue; 
    volatile boolean exitFlag; 

    QueueTest() 
    { 
     for(int i=0; i<10000; i++) 
      input += "abcde"; 
     input += "X"; 
     pos = 0; 
     queue = new SynchronousQueue<String>(); 
     exitFlag = false; 
    } 

    public static void main(String[] args) { 
     QueueTest qtest = new QueueTest(); 
     qtest.runTest(); 
    } 

    void runTest() 
    { 
     Thread producer = new Thread(new Producer()); 
     Thread consumer = new Thread(new Consumer()); 
     producer.start(); 
     consumer.start(); 
     try { 
      producer.join(); 
      consumer.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    class Producer implements Runnable 
    { 
     public void run() 
     { 
      while(true) 
      { 
       String s = read(); 
       if(s.equals("X")) 
        break; 
       try { 
        queue.put(s); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
      exitFlag = true; 
     } 
    } 

    class Consumer implements Runnable 
    { 
     public void run() 
     { 
      while(exitFlag == false) 
      { 
       String s = null; 
       try { 
        s = queue.take(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       process(s); 
      } 
     } 
    } 

    String read() 
    { 
     String str = input.substring(pos, pos+1); 
     pos++; 
     return str; 
    } 

    void process(String s) 
    { 
     long sum = 0; 
     for(long i=0; i<1000; i++) 
      sum = sum * i + i; 
    } 
} 

문제는 실행이 교착 상태처럼 붙어있다. 이 간단한 코드에 버그가 있습니까?

답변

1

경쟁 조건이 더 나을 가능성이 큽니다. exitFlag이 그것에서 나사 2 읽기 전에 false로 설정되지 않은 이후 영구적으로 앉을이 경우 스레드 2의 시나리오

Thread 1 put into queue 
Thread 2 takes out of queue quickly processes and awaits another put from thread 1 
Thread 1 finishes and sets exitFlag to true 

을 상상해보십시오.

포이즌 알약을 고려해 볼 수 있습니다. 우리가 완료 한 다른 스레드에 대한 메시지입니다. 예를 들면 다음과 같습니다.

final String POISON_PILL = " POISON"; 

class Producer implements Runnable { 
    public void run() { 
     while (true) { 
      String s = read(); 
      if (s.equals("X")) 
       break; 
      try { 
       queue.put(s); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
     try { 
      queue.put(POISON_PILL); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 

class Consumer implements Runnable { 
    public void run() { 
     String s = null; 
     try { 
      while ((s = queue.take()) != POISON_PILL) { 
       process(s); 
      } 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 

다른 스레드가 통지되면 다른 스레드가 완료되면 두 스레드가 모두 정상적으로 종료되어야합니다.

+0

는 솔루션을 향해 OP를 도우려고 노력했습니다 ... – jtahlborn

+0

이것은 정말 좋은 생각입니다. 나는 그것을 지금 시도 할 것이다. – JackWM

+0

그것은 작동합니다. 고마워요! 좋은 교훈. – JackWM

0

exitFlag가 여러 스레드에서 공유되므로 개발자가 (Java 메모리 모델 측면에서) 소비자에게 표시 할 수 있도록 제작자가 업데이트해야하는 작업을 수행해야합니다. 이 예에서는 값을 휘발성으로 설정하면 충분합니다.

UPDATE :

당신은 당신의 중단 코드의 스택 덤프를 생성해야합니다. 그러면 무슨 일이 일어나고 있는지 알 수 있습니다. 이 코드는 BlockingQueue와 함께 컨트롤에 플래그를 사용하지 않아야하는 이유에 대한 좋은 예입니다.

업데이트 2 :

부랑자는 가방에서 고양이를 보자 @JohnVint. 예, 독약은이 경쟁 조건에 대한 해결책입니다.

+0

좋은 제안, 나는 그것을 추가했지만 문제는 동일합니다. – JackWM

+0

구체적으로 무엇이 문제입니까? – jtahlborn

+0

끝내지 않고 붙어 있습니다. – JackWM

0

여러분의 프로그램은 다음과 같은 시나리오에 갇혀됩니다 단지 existFlag이 사실입니다 여부를 소비자를 확인 후 (새로운 요소를 넣어하지 않고) exitFlag을 설정

프로듀서. 대기열에 더 이상 요소가 없으면 (소비자가 이전에 모든 요소를 ​​처리 할 수있게 된 경우) queue.take()에서 차단됩니다.

차단 방법이없는 queue.poll()을 사용할 수 있습니다. 프로그램을 조금 변경해야합니다.