Why is unlocking called after calling signal in Ja

2019-08-20 22:58发布

问题:

In Java, why is unlocking called (lock.unlock()) after calling signal (notEmpty.signal() and notFull.signal())?

The order is reversed in pthread. So why is the difference? Thanks.

From The Art of Multiprocessor Programming,

1 class LockedQueue<T> {
2     final Lock lock = new ReentrantLock();
3     final Condition notFull = lock.newCondition();
4     final Condition notEmpty = lock.newCondition();
5     final T[] items;
6     int tail, head, count;
7     public LockedQueue(int capacity) {
8         items = (T[])new Object[capacity];
9     }

10     public void enq(T x) {
11         lock.lock();
12         try {
13             while (count == items.length)
14                 notFull.await();
15             items[tail] = x;
16             if (++tail == items.length)
17                 tail = 0;
18             ++count;
19             notEmpty.signal();
20         } finally {
21             lock.unlock();
22         }
23     }

24     public T deq() {
25         lock.lock();
26         try {
27             while (count == 0)
28                 notEmpty.await();
29             T x = items[head];
30             if (++head == items.length)
31                 head = 0;
32             --count;
33             notFull.signal();
34             return x;
35         } finally {
36             lock.unlock();
37         }
38     }
39 }

回答1:

Edit: Condition can be considered as wrapper for Object. But it is more, because it is connected with lock. But like in Object, there are 2 methods, wait() (await()) for work, and notifyAll() (signal()) the work is done. Look at this stack answer Why should the notify method be inside a synchronized block?

In the code you provided, there are some cases, when thread cannot continue. If the queue is full, and you want to add element, then it cannot proceed. You can do this without await(), and signals(), but then your code would have to return fail, or throw exception for example QueueIsFullException.

Callers of the code want to have an easy interface. They want to just put element in, instead of doing while loop and trying to insert element (busy spinning, cpu intensive). Thats why there is await and signal (wait until ready, without consuming CPU).

So when thread1 is stuck waiting for a place to insert element (await on line 14), it releases the lock, and other threads are free to obtain it.

When thread2 takes the element, it signals to thread1, that queue is not full. So when thread2 unlocks, thread1 reacquires the lock.