标题起得有些奇怪,好端端的为什么要替换wait和notify?



在论坛看到了这么一段:

14. 为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用?
当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。



但这并不是Java语义上的限制,即便写成如下这种形式(摘自爆栈某人提问:wait/notify这玩意儿到底怎么用?),编译仍然会通过无误:

public void waiting() {
	for (int i = 0; i < 10; i++) {
		if (i == 5)
			try {
				this.wait();
			} catch (InterruptedException e) {

        		}
                else
	        	System.out.println(i);
	}
	System.out.println("notify me now");
	this.notify();
}



我不认为会有人在调用某个方法时总是先确认一下javadoc....我认为从IDE列出来的方法提示里选择一个像那么回事的方法执行一遍再确认文档比较符合正常人的习惯。

有人提出这样的问题也说明了一个问题:wait/notify用起来确实很晦涩。



如果觉得还不够晦涩,认为"只要在synchronized块里面用就可以了",其实并不是这样,参考javadoc中的一段:

A thread can also wake up without being notified, interrupted, or 

timing out, a so-called spurious wakeup.  While this will rarely 

occur in practice, applications must guard against it by testing for 

the condition that should have caused the thread to be awakened, and 

continuing to wait if the condition is not satisfied.  In other words,

waits should always occur in loops, like this one:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait(timeout);
        // ... Perform action appropriate to condition
}



下面是一个线程莫名苏醒的情况:

  1. 在某个线程调用notify后和等待线程苏醒的这段时间之内另一个线程得到了锁并改变了受保护的状态。

  2. 其他线程恶意(也就是破坏可变性条件)调用notify或者直接notifyAll.

  3. 伪唤醒 (http://en.wikipedia.org/wiki/Spurious_wakeup).



自Java 1.5发行,JDK提供了更高级的并发工具,这些工具可以更优雅地解决以往只有wait+notify才能解决的问题。

所谓“更高级的并发工具”包括三类:

  1. Executor Framework 

  2. 并发集合

  3. 同步器 (Latch,Barrier,Semaphor什么的...)



对于新代码,如今几乎很少有需要使用wait/notify的情况。

如果必须使用wait/notify,则需要保证是在循环里调用wait,尽管这感觉很蠢。

而为了防止活跃性问题,还是多加利用Java 5之后的各种工具吧。