notify和notifyAll的一段儿代码分析

ImportNew  •  扫码分享
我是创始人李岩:很抱歉!给自己产品做个广告,点击进来看看。  

当你Google”notify()和notifyAll()的区别”时,会有大片的结果弹出来,(这里先把jdk的javadoc文档那一段撇开不说),所有这些搜索结果归结为等待的线程被唤醒的数量:notify()是唤醒一个, 而notifyall()是唤醒全部.那他们的真正区别是什么呢?

public synchronized void put(Object o) {
    while ( buf.size() == MAX_SIZE) {
         wait(); // 如果buffer为Null,就会执行wait方法等待(为了简单,我们省略try/catch语句块)
    }
    buf.add(o);
    notify(); // 通知所有正在等待对象锁的Producer和Consumer(译者注:包括被阻挡在方法外的Producer和Consumer)
}

public synchronized Object get() {
     // Y: 这里是C2获取锁的地方(在get方法之前)
    while ( buf.size() == 0) {
         wait(); // 如果buffer为Null,就会执行wait方法(为了简单,同样省略try/catch语句块)
          // X: 这里是C1试图重新获得锁的地方(看下面代码)
    }
    Object o = buf.remove(0);
    notify(); // 通知所有正在等待对象锁的Producer和Consumer(译者注:包括被阻挡在方法外的Producer和Consumer)
    return o;
}

消费者1(C1)进入同步块中,此时buf是空的,所以C1被放入阻塞队列中(译者注:此时被卡在X上面),消费者2(C2)将要进入同步方法中(在Y的上面),但是由于有线程在运行,所以只能被阻挡在Y处的上面。然后,生产者P1将一个对象放入到buf中,又调用notify方法。此时阻塞队列中唯一的线程是C1(译者注:C2不在阻塞队列中),所以C1被唤醒,C1被唤醒之后又开始试图重新获得对象锁,此时C1还在X的上面。

现在的情况是,C1和C2都在试图去获取同步锁,这两个线程会有一个被选择进入方法,另一个则会被堵塞(译者注:C1已经在方法中,不过还是会和C2竞争锁,如果C2获得锁,则C2进入方法执行以下操作,而C1还是继续等待锁;如果C1获得锁,则C1往下执行,而C2还是会被挡在方法外面)。假如C2先获得了对象锁,C1仍然被阻挡着(此时C1还试图在X处获得锁,所以被挡在X上面),C2完成了方法,并释放了锁。现在C1获得了锁。假设这里没有while循环,那么C1就会往下执行,从buf中删除一个对象,但是此时buf中已经没有对象了,因为刚刚C2已经取走了一个对象,如果此时C1执行buf.remove(0),则会报IndexArrayOutOfBoundsException异常。为了防止这样的异常发生,我们在上面用到了while循环,在往下执行之前,判断此时buf的大小是否为0,如果不是0,则往下执行,如果是0,则继续wait()。

随意打赏

提交建议
微信扫一扫,分享给好友吧。