Java中的wait()与notify()/notifyAll()

1、wait()与sleep()/yield()的不同

调用sleep()和yield()时,线程掌握的对象上的锁不会被释放;而调用wait()时,线程掌握的对象上的锁会被释放掉。这一点是需要注意的,也是有意义的。因为调用wait()会释放锁,所以在一个synchronized方法或代码段中调用wait()就意味着该对象中的其它synchronized方法或代码段可以得到执行。这一点至关重要,因为这些操作可以会导致对象发生改变,而这些改变往往是唤醒wait()等待的条件。

2、wait()/notify()/notifyAll()的调用

wait()/notify()/notifyAll()是Object类的一部分,也就是说这些操作是所有对象上都会有的操作,这是有道理的,因为这些操作都伴随着对对象上锁的操作。事实上,只能在synchronized方法或者代码段中调用这些方法(因为不用操作锁,所以sleep()可以在非同步方法里被调用)。如果在非synchronized方法或者代码段中调用这些方法,编译的时候能够通过,但是运行的时候会抛出IllegalMonitorStateException异常,同时伴随着一些含糊的信息,比如“当前线程不是拥有者”。消息的意思是,调用wait()、notify()和notifyAll()之前线程必须掌握对象的锁。

3、notify()与notifyAll()的差别

notify()调用时,如果只有一个线程在当前对象上调用了wait(),这个wait()的调用线程就会被唤醒;如果有多个线程调用了当前对象上的wait()方法,那么这些线程中只有一个会被唤醒,至于唤醒哪一个线程对程序员是未知的。而notifyAll()被调用后,所有调用了当前对象上的wait()的在等待的线程都会被唤醒,这些被唤醒的线程将会重新尝试去获取当前对象上的锁。当然只有一个线程能够获取锁并得到执行,剩下的线程将会被阻塞,需要注意的是这里这些因为竞争没能成功获取锁的线程是处于阻塞状态的,而不是调用wait()后等待被唤醒的状态,它们只要在接下来的执行过程中成功获取锁就可以继续执行,而不要再等待notify()/notifyAll()被调用唤醒它们。

需要注意的是,正常情况下应该使用的是notifyAll(),使用notify()而不是notifyAll()可以被看成是一种优化。因为在当前对象上可能有很多线程调用了wait()在等待,所以使用notifyAll()而不是notify()是更安全的。那么什么时候可以使用notify()呢?如果要使用notify(),那么就需要保证所有的线程等待的都是相同的条件(并且保证在可能的子类中这个条件也是被满足的)。因为只有这样,不论哪个线程被唤醒,这个被唤醒的线程都是恰当的线程,它和其它等待的线程等待的条件都是一样的。如果不同的线程等待的是不同的条件,调用notify()后你就不知道具体等待哪个条件的线程被唤醒了。这时,你就应该调用notifyAll()唤醒所有等待的线程,然后让它们自己在接下来的执行过程中再去判断条件,决定是不是要重新进入到wait()状态。这就涉及到wait()的惯用法。

4、wait()调用的惯用法

在调用wait()时必须使用一个检查感兴趣的条件的while循环包围wait()。这很重要:
- 可能会有不同的线程出于相同的原因(关注相同的条件)在等待同一个锁,第一个被唤醒的线程可能会改变关注的条件(即使你没有那么做,也会有继承你的子类去这样做)。如果属于这种情况,那么这个人物就应该在再次挂起,直至感兴趣的条件发生变化。
- 在一个线程被唤醒的时刻,有可能会有某个其它的线程已经对等待的条件做出改变,从而使得当前的线程不能被执行或者没有必要在执行。此时,应该通过调用wait()将其重新挂起。
- 可能有一组线程出于不同的原因(关注不同的条件)在等待同一个锁(这是就需要使用notifyAll())。这种情况下,所有的线程都会被唤醒,它们需要检查一下是不是自己等待的条件发生了,如果不是就应该再次调用wait()挂起等待。

使用while循环包围wait()的实质就是在线程被唤醒后需要再次检查一下等待的条件,如果条件不满足则应该重新调用wait()继续等待。

    synchronized(sharedMonitor){
        while(!condition){
            sharedMonitor.wait();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值