使用方法
在了解使用方法之前,要先知道一个概念——等待队列。
所有实例都拥有一个等待队列,它是在实例的wait方法执行后停止的线程的队列。
wait()等待方法
wait()方法的作用就是让调用线程暂停进入对应的实例的等待队列中
例如:
obj.wait()就是让当前线程停止并进入对象obj的等待队列中,
而通常的省略obj的写法:wait()实际上是等价于this.wait()。
notify()通知方法
notify()方法通知某对象等待队列中的一个线程从等待队列中取出。
例如:
obj.notify()就是让对象obj的等待队列中的一个线程出队(并不会指定是哪个线程),
同理省略obj写法:notify()等价于this.notify()。
notifyAll()通知全部方法
这个方法与notify()唯一的不同是notify()只唤醒一个线程而它唤醒全部线程。
注意事项
wait()方法注意事项
1、wait()不会自动唤醒,必须等到notify/notifyAll
2、调用wait()方法线程必须要持有锁
这点要求我们要把wait方法写在被synchronized保护的同步代码中。
这样做的主要原因就是为了保证线程安全。而如果不这样做会产生什么问题呢
class PCM{
ArrayList<String> arrayList = new ArrayList<String>();
public void add(String something){
arrayList.add(something);
notify();
}
public String take() throws InterruptedException {
while(arrayList.isEmpty()){
wait();
}
return arrayList.remove(0);
}
}
一段典型的生产者消费者模型
这段代码可能发生的最大问题就是:
当调用take方法判断arrayList是否为空时得到了true也就是arrayList为空。而这时在进入wait等待前,调度器就暂停了这个线程。注意此时wait还没执行。
而后add开始执行,它将一些东西添加进了arrayList,并调用了notify。此时notify并不会产生任何作用。
最后调度器将暂停的take方法继续执行,就出现了问题 wait等待开始了。但这时就进入了永久的等待(如果只有一个生产者)
会发生这一切的原因就是while判断和wait等待并不是一个synchronized的操作或者说不是一个原子的操作。将判断与执行做了拆分在并发条件下就会产生很多意料之外的事情。
3、线程一进入等待队列会释放锁(和sleep()不同 sleep不会释放锁)
这里额外提一句wait和sleep的区别:
1、wait要写在synchronized中,sleep没有要求
2、sleep不会释放锁,wait会
3、wait方法除非被notify/notifyAll唤醒不然会一直等待,
而sleep要设置一个等待时间。(这可能就是自然醒和被人叫醒的区别吧#狗头)
4、wait是Object类的方法,sleep是Thread类的方法
notify()/notifyAll()
1、要执行notify/notifyAll方法必须持有所调用实例的锁(与wait对应)
2、当等待队列中线程不只一个时,notify选择线程是否按照wait等待顺序来唤醒在虚拟机规范中并没有规定。所以不应该指望notify来做出正确的选择。
3、被notify和notifyAll唤醒的线程不会立即执行。(因为此时执行notify的线程还持有者锁)要等待持锁线程释放锁。