首先,jvm内部在notify与wait逻辑中都强制判断是否有对等待对象加锁,所以如下代码逻辑会抛异常
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对象进行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;
}
}
}
修改后的正确代码实现为:
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;
}
}
}
要实现条件队列细化,必须使用ReentrantLock与condition这对叼逼组合
jvm为什么强制wait/notify必须包裹在synchronized块中
queue.wait();这句话被调用时,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.