首先,jvm內(nèi)部在notify與wait邏輯中都強(qiáng)制判斷是否有對等待對象加鎖,所以如下代碼邏輯會拋異常
static class BlockingQ {
private static final int MAX_SIZE = 10;
private final List<String> queue = new ArrayList<>(MAX_SIZE);
private final Object putWait = new Object();
private final Object getWait = new Object();
public void put(String s) throws InterruptedException {
synchronized (queue) { //這里鎖住的是queue對象
while (queue.size() >= MAX_SIZE)
putWait.wait(); //這里的wait必須是針對queue對象進(jìn)行wait
queue.add(s);
getWait.notifyAll(); //同理
}
}
public String get() throws InterruptedException {
synchronized (queue) {
while (queue.isEmpty()) {
getWait.wait(); //同理
}
String result = queue.remove(0);
putWait.notifyAll(); //同理
return result;
}
}
}
修改后的正確代碼實(shí)現(xiàn)為:
static class BlockingQ {
private static final int MAX_SIZE = 10;
private final List<String> queue = new ArrayList<>(MAX_SIZE);
public void put(String s) throws InterruptedException {
synchronized (queue) {
while (queue.size() >= MAX_SIZE)
queue.wait();
queue.add(s);
queue.notifyAll();
}
}
public String get() throws InterruptedException {
synchronized (queue) {
while (queue.isEmpty()) {
queue.wait();
}
String result = queue.remove(0);
queue.notifyAll();
return result;
}
}
}
要實(shí)現(xiàn)條件隊(duì)列細(xì)化,必須使用ReentrantLock與condition這對叼逼組合
jvm為什么強(qiáng)制wait/notify必須包裹在synchronized塊中
queue.wait();這句話被調(diào)用時,jvm會釋放自身這把鎖,如果沒有同步鎖或者鎖不是自己這個對象,將會報錯。
原因-----------摘自stackoverflow
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
This is what could potentially happen:
1.A consumer thread calls take() and sees that the buffer.isEmpty().
2.Before the consumer thread goes on to call wait(), a producer thread comes along and invokes a full give(), that is, buffer.add(data); notify();
3.The consumer thread will now call wait() (and miss the notify() that was just called).
4.If unlucky, the producer thread won't produce more give() as a result of the fact that the consumer thread never wakes up, and we have a dead-lock.
Once you understand the issue, the solution is obvious: Always perform give/notify and isEmpty/wait atomically.