1. 使用对象锁(如,lock)作为同步对象
需要执行wait操作的线程,在线程体中用锁对象(即lock)去执行wait,即lock.wait().
执行中的线程,只有获得对象锁,才能调用notify和notifyAll方法,调用是使用对象锁来调用的,即lock.nitifyAll().
package thrds;
public class EightThrd {
public static void main(String args[]) {
Thread t1 = new Thread(new ThTst4());
Thread t2 = new Thread(new ThTst4());
t1.start();
t2.start();
try {
Thread.sleep(20000);
synchronized (ThTst4.lock) // 只有获得了对象锁的线程才能调用该 对象锁的notify/notifyAll方法
{
ThTst4.lock.notifyAll();
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
class ThTst4 extends Thread {
public final static String lock = "lock"; //public static 达到公用的目的
public void run() {
tst();
}
synchronized void tst() {
synchronized (lock)
{
System.out.println(Thread.currentThread().getId() + " in");
try {
Thread.sleep(5000);
lock.wait();
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId() + " out");
}
}
}
输出:
14 in
16 in
16 out
14 out
2. 使用类定义(如,Integer.class)作为同步对象,不能调用wait和notify,notifyAll方法
package thrds;
public class NineThrd {
public static void main(String args[]) {
ThTst5 tobj = new ThTst5();
Thread t1 = new Thread(tobj);
Thread t2 = new Thread(tobj);
t1.start();
t2.start();
try {
Thread.sleep(20000);
synchronized (Integer.class) // 只有获得了类定义锁的线程才能调用该notify/notifyAll方法,在哪个对象上wait的,在哪个对象上调用notifyAll。 本例中由于锁和调用这三个方法的对象不是同一个,因此本例是失败的
{
tobj.notifyAll();
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
class ThTst5 extends Thread {
public void run() {
tst();
}
synchronized void tst() {
synchronized (Integer.class)
{
System.out.println(Thread.currentThread().getId() + " in");
try {
Thread.sleep(5000);
this.wait();
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId() + " out");
}
}
}
输出:(这种方式有问题,锁和调用wait的对象,不是同一个。 本例中由于锁和调用这三个方法的对象不是同一个,因此是失败的)
14 in
将锁改为线程对象就可以了。
如下:
package thrds;
public class NineThrd {
public static void main(String args[]) {
ThTst5 tobj = new ThTst5();
Thread t1 = new Thread(tobj);
Thread t2 = new Thread(tobj);
t1.start();
t2.start();
try {
Thread.sleep(20000);
synchronized (tobj) // 只有获得了类定义锁的线程才能调用该notify/notifyAll方法,在哪个对象上wait的,在哪个对象上调用notifyAll.
{
tobj.notifyAll();
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
class ThTst5 extends Thread {
public void run() {
tst();
}
synchronized void tst() {
synchronized (this)
{
System.out.println(Thread.currentThread().getId() + " in");
try {
Thread.sleep(5000);
this.wait();
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId() + " out");
}
}
}
输出:
14 in
15 in
15 out
14 out
注意:
1. 只有获得了对象锁lock才能调用 wait,notify, notifyAll,调用这三个函数,用对象锁来调用,即lock.wait(), lock.notify(), lock.notifyAll().
2. 调用wait的对象,和调用notify,notifyAll的对象必须是同一个对象,而且这个对象必须和对象锁是同一个对象。
3. 恢复的线程是由wait语句的下一句开始执行的,恢复的线程已经取得了对象锁, 不会再去申请对象锁。调用notifyAll 后,虽然通知到在该对象锁上的所有等待线程,即所有线程都被唤醒了,开始参与CPU的竞争。但只有一个线程获得了执行权,该线程执行结束后,其他被notifyAll唤醒的线程开始竞争对象锁,竞争到锁的线程,仍然是从wait的下一句开始执行的,而不是从获得锁的下一句执行。
以下摘自网友解答:
首先: 使用wait方法和使用synchornized来分配cpu时间是有本质区别的。wait会释放锁,synchornized不释放锁。
还有:(wait/notify/notifyAll)只能在取得对象锁的时候才能调用。
调用notifyAll通知所有线程继续执行,该锁对象上wait的所有线程都被唤醒,但只能有一个线程在执行,其余的线程在等待(唤醒状态了)。这时的等待和调用notifyAll前的等待是不一样的。
notifyAll前:在对象上休息区内休息
notifyAll后:在排队等待获得对象锁。
notify和notifyAll都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个,但究竟是哪一个不能确定,而notifyAll则唤醒这个对象上的休息室中所有的线程.
一般有为了安全性,我们在绝对多数时候应该使用notifiAll(),除非你明确知道只唤醒其中的一个线程.
至于有些书上说“notify:唤醒同一对象监视器中调用wait的第一个线程”我认为是没有根据的因为sun公司是这样说的“The choice is arbitrary and occurs at the discretion of theimplementation.”
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
wait(),notify()和notifyAll()都是java.lang.Object的方法:
wait(): Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
notify(): Wakes up a single thread that is waiting on this object's monitor.
notifyAll(): Wakes up all threads that are waiting on this object's monitor.
这三个方法,都是Java语言提供的实现线程间阻塞(Blocking)和控制进程内调度(inter-process communication)的底层机制。在解释如何使用前,先说明一下两点:
1. 正如Java内任何对象都能成为锁(Lock)一样,任何对象也都能成为条件队列(Condition queue)。而这个对象里的wait(), notify()和notifyAll()则是这个条件队列的固有(intrinsic)的方法。
2. 一个对象的固有锁和它的固有条件队列是相关的,为了调用对象X内条件队列的方法,你必须获得对象X的锁。这是因为等待状态条件的机制和保证状态连续性的机制是紧密的结合在一起的。
(An object's intrinsic lock and its intrinsic condition queue are related: in order to call any of the condition queue methods on object X, you must hold the lock on X. This is because the mechanism for waiting for state-based conditions is necessarily tightly bound to the mechanism fo preserving state consistency)
根据上述两点,在调用wait(), notify()或notifyAll()的时候,必须先获得锁,且状态变量须由该锁保护,而固有锁对象与固有条件队列对象又是同一个对象。也就是说,要在某个对象上执行wait,notify,先必须锁定该对象,而对应的状态变量也是由该对象锁保护的。
知道怎么使用后,我们来问下面的问题:
1. 执行wait, notify时,不获得锁会如何?
请看代码:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
obj.wait();
obj.notifyAll();
}
执行以上代码,会抛出java.lang.IllegalMonitorStateException的异常。
2. 执行wait, notify时,不获得该对象的锁会如何?
请看代码:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Object lock = new Object();
synchronized (lock) {
obj.wait();
obj.notifyAll();
}
}
执行代码,同样会抛出java.lang.IllegalMonitorStateException的异常。