我们一般的写法就是 对象的 wait 或者 Condition 的 await 方法都放在 同步块中,为什么要这样呢?
可能有人说不这样会报 IllegalMonitorStatesException,这个应该是结果而不是原因,面试中这样说肯定是不成的。
要说明这个问题,我这里用生产、消费者模型来做说明,用的是 Condition 的 await 和 signal 方法。
下面直接来看代码:
class ConAndProService{
private final static Stack<Integer> list = new Stack<>(); //list里面只存放一个数据
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void producer(){
try {
lock.lock();
while (list.size() == 1){ //注1
condition.await();
}
int val = new Random().nextInt(10)+1;
System.out.println("ThreadName : "+Thread.currentThread().getName()+" ...........producer val ........ = "+val);
list.add(val);
condition.signal(); //注2
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void consumer(){
try {
lock.lock();
while (list.size() == 0){//注3
condition.await();//注4
}
int val = list.pop();
System.out.println("ThreadName : "+Thread.currentThread().getName()+"*************** consumer val *************** = "+val);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
现在我们假设 没有加 同步块 ,那么就会发生下面的结果:
1. 假设消费者先开始执行,执行完注释3 , 但是并没有执行注释4就发生上下文切换,执行到注释1 ;
2. 因为 list 里面没值 所以往里面 add 然后 执行注释 2,发出 signal,由于此时 消费者 还没有执行 wait 所以signal 无效;
3. 再次发生上下文切换,由于注3 之前执行过了,直接 await 了,消费者线程阻塞了;
4. 然后再次执行注释1,由于 list中的数据没有被消费掉,所以执行 await 导致 生产者线程也阻塞了。
如果有同步块就不会发生上述情况了, 因为同步块同一时间只能有一个线程可以执行。