和风细雨

世上本无难事,心以为难,斯乃真难。苟不存一难之见于心,则运用之术自出。

线程的协调

本文内容

Java进行线程调度的方法.
如何让线程进入等待.
wait set概念的理解.
如何激活等待状态的线程.

进行线程协调的方法

从上一章《线程的互斥》中我们知道,当方法体或代码块被synchronized方法修饰时,有一个线程在执行这部分代码时,其它线程无法同时执行这部分代码。但如果我们想进行更高效的处理如主动调整线程而不是让线程被动等待和盲目竞争时该怎么处理呢?
在Java中,有三个方法可以对线程进行主动高效的控制,它们是wait,notify和notifyAll。
wait是主动让线程等待的方法,而notify和notifyAll则是激活等待中的线程的方法。他们都是Object类的方法,也就是说任何对象都可以使用这三个方法。

Wait Set-线程的休息室

在学习使用wait,notify和notifyAll这三个方法之前,我们可以先理解一下Wait Set的概念,它是一个在某实例执行wait方法时,停止操作的线程的集合,类似于线程的休息室,每个实例都拥有这样一个休息室。
Wait方法是用来把线程请入这个休息室的,而notify和notifyAll这两个方法是用来将进入休息室休息的线程激活的。
wait Set是一个虚拟的概念,它既不是实例的字段,也不是可以获取在实例上wait中线程的列表的方法.它只是用来帮助我们理解线程的等待和激活的。

Wait方法,将线程放入Wait Set

使用Wait方法时,线程即进入Wait set。如线程在执行代码时遇到这样的语句:xxObj.wait();则目前的线程会暂时停止运行,进入实例xxObj的wait Set.
当线程进入Wait Set时,即释放了对该实例的锁定.也就是说,即使是被synchronized修饰的方法和代码块,当第一个线程进入实例的wait Set等待后,其它线程就可以再进入这部分代码了.
wait()前如果不写某对象表示其前面的对象是this, wait()=this.wait();

notify方法-从wait set中激活一个线程

使用notify方法时,程序会从处于等待的实例的休息室中激活一个线程.代码如下:
xxObj.notify();程序将从xxObj的wait set中挑出一个激活.这个线程即准备退出wait set.当当前线程释放对xxObj的锁定后,这个线程即获取这个锁定,从上次的停止点-执行wait的地方开始运行。
线程必须有调用的实例的锁定,才能执行notify方法.
Wait set中处于等待状态的线程有多个,具体激活那一个依环境和系统而变,事先无法辩明.我们大致可理解为随机挑选了一个.

notifyAll方法-激活wait set中所有等待的线程

当执行notifyAll方法时,实例中所有处于等待状态的线程都会被激活.代码为:
xxObj.notifyAll();执行此代码后xxObj的wait set中所有处于等待中的线程都会被激活.但具体那个线程获取执行wait方法时释放的锁定要靠竞争,最终只能有一个线程获得锁定,其它的线程只能继续回去等待.
notifyAll与notify的差异在于notifyAll激活所有wait set中等待的线程,而notify只激活其中的一个.

该使用notify还是notifyAll

建议:
1) 选择notifyAll比notify稳当安全,如果notify处理得不好,程序会有隐患.
2) 选择notifyAll比notify可靠,是大多数程序的首选.
3) 当你对代码已经很清楚,对线程理解也很透彻时,你可以选择使用notify,发挥其处理速度高的优势.

当前线程必须持有欲调用实例的锁定,才能调用wait,notify和notifyAll这三个方法.

如果代码是xxObj.notifyAll(或wait, notify)(),则这行代码必须处于synchronized(xxObj){…}代码块中.
如果代码是this.notifyAll(或wait, notify)(),则这行代码必须处于synchronized修饰的方法中.
前面说过, notifyAll和notify会激活线程去获得进入wait时释放的锁定,但这个锁定要等刚才执行notifyAll或notify方法的线程释放这个锁定才能获取.

总结

1) wait,notify和notifyAll都是java.lang.Object的方法,它们用来对线程进行调度.
2) obj.wait()是把执行这句的线程放入obj的wait set中.
3) obj.notify()是从wait set中唤醒一个线程,这个线程在当前线程释放对obj的锁定后即获取这个锁定.
4) obj.notifyAll()是唤醒所有在obj的wait set中的线程,这批线程在当前线程释放obj的锁定后去竞争这个锁定,最终只有一个能获得,其它的又重新返回wait set等待下一次激活.
5) 执行这个wait,notify和notifyAll这三个方法前,当前线程(即执行obj.wait(), obj.notify()和obj.notifyAll()代码的线程)必须持有obj的锁定.

posted on 2008-02-22 14:13 和风细雨 阅读(318) 评论(0)  编辑  收藏 所属分类: 线程


只有注册用户登录后才能发表评论。


网站导航: