2016-08-23 1 views
2

어리석은 질문 일지 모르지만이 클래스의 두 개의 다른 스레드가 단일 ReentrantLock을 사용할 수없는 이유를 이해할 수 없습니다 (사용 된 ArrayBlockingQueue의 단순화 된 솔루션입니다). 스레드와 잠금 장치)와 재생 :ArrayBlockingQueue의 두 스레드가 ReentrantLock을 취득 할 수있는 이유는 무엇입니까?

pool-1-thread-1: acquired lock in put() 
pool-1-thread-1: released lock for put() 
pool-1-thread-1: acquired lock in put() - this guy has taken the lock 
pool-1-thread-2: acquired lock in take() - that guy has taken the lock as well! wtf? 
pool-1-thread-2: released lock for take() 
Value = 0 
pool-1-thread-2: acquired lock in take() 
pool-1-thread-1: released lock for put() 
pool-1-thread-1: acquired lock in put() 
pool-1-thread-2: released lock for take() 
Value = 1 
pool-1-thread-1: released lock for put() 
pool-1-thread-1: acquired lock in put() 
pool-1-thread-2: acquired lock in take() 
pool-1-thread-2: released lock for take() 
Value = 2 
... 

나는 그것이 조건의 내가 왜 발생합니까 이해할 수없는 논리적주기 동안의 사용 때문이 아니라 용의자 :

class SampleThreadSafeQueue { 

     private ReentrantLock lock = new ReentrantLock(); 
     private List<Integer> list = new ArrayList<>(); 
     private Condition notEmpty; 
     private Condition notFull; 
     private volatile int count; 

     public SampleThreadSafeQueue() { 
      notEmpty = lock.newCondition(); 
      notFull = lock.newCondition(); 
     } 

     public int take() { 
      try { 
       lock.lock(); 
       System.out.println(Thread.currentThread().getName() +": acquired lock in take()"); 
       while (count == 0) { 
        notEmpty.await(); 
       } 

       return extract(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
       return 0; 
      } finally { 
       System.out.println(Thread.currentThread().getName() +": released lock for take()"); 
       lock.unlock(); 
      } 
     } 

     private int extract() { 
      int index = count <= 0 ? 0 : count - 1; 
      Integer integer = list.get(index); 
      list.remove(index); 
      count--; 
      list.clear(); 
      notFull.signal(); 
      return integer; 
     } 

     public void put(int value) { 
      try { 
       lock.lock(); 
       System.out.println(Thread.currentThread().getName() +": acquired lock in put()"); 
       while (!list.isEmpty()) { 
        notFull.await(); 
       } 
       Thread.sleep(3000); // let's assume it takes 3 secs to add value 
       insert(value); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } finally { 
       System.out.println(Thread.currentThread().getName() +": released lock for put()"); 
       lock.unlock(); 
      } 
     } 


     private void insert(int value) { 
      list.add(value); 
      count = list.size(); 
      notEmpty.signal(); 
     } 
    } 

을 나는 콘솔에서이 결과를받을 . 설명에 감사드립니다. 감사!

답변

1

은 당신이 어떤 자물쇠로부터 취득한 Conditionawait()를 호출하면 스레드가 잠금을 해제하고 Condition이 (signal() 또는 signalAll()에 의해) 다른 스레드로부터 통지 될 때까지 정지한다.

따라서 스레드 1은 잠금을 획득하지만 await()을 호출하고 대기 모드로 전환하여 최근에 스레드 2에서 획득 한 잠금을 해제합니다. 스레드 2가 완료되면 스레드 1에이를 알립니다 (signal() 호출) 자물쇠를 해제합니다. 잠금 장치는 스레드 1에 의해 즉시 재 획득되며, 작업을 깨우고 계속해서 잠금을 해제합니다.

그런 다음 약간 다른 순서로 반복합니다.

1

은 당신이 알아야하는 조건 (기다리고 있습니다 신호) 동기화 (통보 기다려)과 같은 점이다 조건 문서 http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html

를 살펴 보자. await()으로 전화하면 잠금 장치가 해제됩니다. (당신은 여전히 ​​lock.unlock() tho로 전화해야합니다). put() 함수는 notFull.signal()을 트리거 할 take()을 기다리고 put()notEmpty.signal()이 트리거 될 때까지 대기합니다. take()이 대기합니다. 잠금 장치는 마치 await() 호출에서 직접 동기화되며 결국은 익숙하지 않은 lock.lock()이 아닙니다.

이 문서화 된 예는 다음과 같습니다

class BoundedBuffer { 
    final Lock lock = new ReentrantLock(); 
    final Condition notFull = lock.newCondition(); 
    final Condition notEmpty = lock.newCondition(); 

    final Object[] items = new Object[100]; 
    int putptr, takeptr, count; 

    public void put(Object x) throws InterruptedException { 
    lock.lock(); 
    try { 
     while (count == items.length) 
     notFull.await(); 
     items[putptr] = x; 
     if (++putptr == items.length) putptr = 0; 
     ++count; 
     notEmpty.signal(); 
    } finally { 
     lock.unlock(); 
    } 
    } 

    public Object take() throws InterruptedException { 
    lock.lock(); 
    try { 
     while (count == 0) 
     notEmpty.await(); 
     Object x = items[takeptr]; 
     if (++takeptr == items.length) takeptr = 0; 
     --count; 
     notFull.signal(); 
     return x; 
    } finally { 
     lock.unlock(); 
    } 
    } 
}