2012-11-11 3 views
2

아래 코드는 임의의 값을 순환 대기열에 삽입하여 제거하려고합니다. 그러나 몇 가지 동기화 문제가 있습니다. 더 높은 수준의 루틴을 사용할 수 있다는 것을 알고 있으며 프로덕션 코드를 위해 그렇게 할 것입니다.하지만 이것이 왜 작동하지 않는지 궁금합니다. 내가 여기서 무엇을 놓치고 있니?이 코드가 illegalMonitorState 예외를 발생시키는 이유는 무엇입니까?

public class CircularQueue { 
int count; 
int rear; 
int front; 
Object lock = new Object(); 
int size; 
int[] array; 
CircularQueue(int size) 
{ 
    this.size= size; 
    array = new int[size]; 
} 

void enqueue(int number) throws InterruptedException 
{ 
    if(isFull()) 
     lock.wait(); 

    synchronized(lock) 
    { 

     array[rear] = number; 
     System.out.println("Rear is:"+ rear+ "value is:"+number+"Size is:"+size); 

     rear = (rear+1)%size; 
     count++; 
    } 
    lock.notify(); 

} 

void dequeue() throws InterruptedException 
{ 
    if(isEmpty()) 
     lock.wait(); 

    synchronized(lock) 
    { 
     int retVal = 0; 
     retVal = array[front]; 
     System.out.println("Front is:"+ front+ "value is:"+retVal); 

     front = (front+1)%size; 
     count--; 
    } 

    lock.notify(); 

} 

boolean isFull() 
{ 
    if(count == size) 
    { 
     return true; 
    } 
    else 
     return false; 

} 

boolean isEmpty() 
{ 
    return count == 0; 
} 
} 

// 테스트 클래스

import java.util.Random; 
public class App { 

    public static void main(String[] args) throws InterruptedException 
    { 
     final Random random = new Random(); 
     final CircularQueue circularQueue = new CircularQueue(10); 
     Thread t1 = new Thread(new Runnable(){ 

     @Override 
     public void run() { 
      try { 
       circularQueue.enqueue(random.nextInt(100)); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 

     }); 
     Thread t2 = new Thread(new Runnable(){ 

      @Override 
      public void run() { 
       try { 
        circularQueue.dequeue(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 

      }); 

     t1.start(); 
     t2.start(); 
     t1.join(); 
     t2.join(); 

    } 

} 

답변

6

java.lang.Object#waitjava.lang.Object#notify, java.lang.Object#notifyAll가 동기화 블록에서 호출해야하기 때문에. 당신이 인접한 호출 할 때까지 기다립니다 -

void enqueue(int number) throws InterruptedException 
{ 

    synchronized(lock) 
    { 
     if(isFull()) 
      lock.wait(); 

     array[rear] = number; 
     System.out.println("Rear is:"+ rear+ "value is:"+number+"Size is:"+size); 

     rear = (rear+1)%size; 
     count++; 
     lock.notify(); 
    } 
} 

void dequeue() throws InterruptedException 
{ 
    synchronized(lock) 
    { 
     if(isEmpty()) 
      lock.wait(); 

     int retVal = 0; 
     retVal = array[front]; 
     System.out.println("Front is:"+ front+ "value is:"+retVal); 

     front = (front+1)%size; 
     count--; 
     lock.notify(); 
    } 

} 
0
이 코드의 또 다른 문제도 IsEmpty 함수 경우/true를 돌려 isFull이다

: 솔루션으로

당신이 동기화 블록 내부에 조건을 놓아야합니다 (확인 필요) 대기열의 상태가 변경 될 수 있습니다. 예를 들어
:
- 큐가 비어
- 스레드 1 IsEmpty 함수()를 호출
- 컨텍스트 스위치
- 스레드 2 호출 (현재 큐가 비어 있지 않은) 대기열에
- 컨텍스트 스위치
- 스레드 1 대기열이 비어 있지 않아도 lock.wait() 이벤트를 호출하지 않습니다.

이 문제는 wait()/notify() 호출이 동기화 된 블록에 배치 될 때 당연히 해결됩니다.

관련 문제