wait(),notify()方法详解

使用场景
在单线程下,要实现程序执行某操作必须满足某一条件可以将这个操作放在一格if的语句体中。在多线程下,一个线程执行的某个操作必须满足某个条件,且这个条件不满足只是暂时的,这个条件只是需要另一个线程来改变。在这种情况下,我们可以在条件不满足的时候将线程暂停进入WAIT,当条件满足的时候,再去将线程唤醒。
使用wait()和notify()实现上述场景

someObject.wait()/someObject.wait(long)

每一个对象都有此方法,对象调用此方法会使得对象所在的线程暂停进入WAIT状态(称此线程为该对象上的等待线程),直到由于条件满足或者超时(wait(long))才被唤醒。但是如果像如下直接调用此方法则会抛出异常
由于一个对象的方法可以被多个线程调用,所以一个对象可以有多个等待线程
someObject.wait()

Exception in thread "main" java.lang.IllegalMonitorStateException
下面来说一说调用此方法需要满足的条件
  • 1,线程必须持有该对象的内部锁

  • 2,保护条件:共享变量组成的表达式

  • 3,执行动作

        synchronized (this){
          while(!保护条件){
              someObject.wait();
          }
          //执行动作
          doAction();
      }
    
前面提到过,在单线程下,我们只需要使用一个 if就可以实现需求。那么为什么在多线程下要加如此多的限制呢?
加锁是为了保障整个操作是原子性的不会出现竞态等线程安全问题。试想没有锁的保护,当一个线程开始因为保护条件不满足而wait,待条件满足后通知线程将他唤醒。这时在他被唤醒到执行doAction的过程中,共享变量又被改变而导致在条件不满足的情况下而执行了doAction(),其实使用while语句体也是为了不出现这种情况,在被唤醒后再次判断。
wait实现:

相当于如下伪代码

    public void wait(){
        if(!Thread.holdsLock(this)){   //检查当前线程是否拥有对象锁
            throw new IllegalMonitorStateException();
        }
        if(!waitSet){ //如果不在等待集合中,加入到等待集合中
            addToWaitSet(Thread.currentThread());
        }
        atomic{//原子操作
            releaseLock(this);     //释放锁
            block(Thread.currentThread());  //暂停当前线程
        }
        acquireLock(this);   //再次申请锁
        removeFromWaitSet(Thread.currentThread()); //申请成功后,将线程从该对象的等待集合中移除
        return;  
    }
java为每一个对象都维护着一个 wait set,用来存放调用wait而暂停的线程的引用。如图所示,当线程调用 wait会进入到等待集中,然后释放锁,进入 wait状态。但次数对 wait方法的调用并没有返回。当条件满足,当前线程必须再次申请锁,申请成功后将线程从wait set中移除,返回。对wait方法的调用结束。

someObject.notify()/someObject.notifyAll()
对象调用notify会唤醒相应对象等待集中任一线程。notifyAll会唤醒该对象等待集中所有的线程。调用该对象方法的线程称为通知线程。同样调用该方法必须持有该对象的内部锁,正是由于调用该方法需要对象内部锁,所以执行wait方法的线程必须在暂停线程后需要释放锁。否则通知线程无法获得锁,通知等待线程,等待线程由于没有通知线程会发生活性故障:死锁。
    synchronized (this){
        updateStatus();
        someObject.notify();
    }

wait/notify所导致的开销和问题
过早唤醒
调用notify(),唤醒该对象的任意一个等待线程,其他线程不管还处于wait状态。而调用 notifyAll()方法会唤醒所有的等待线程导致该对象上不同保护条件的等待线程全部唤醒。这样那些条件不满足的线程也被唤醒,申请锁,移除出wait set ,while不满足,再次释放锁,wait。这就是过早唤醒,而且上下文切换,导致不必要的开销。
信号丢失
上文提到过的,notify不考虑保护条件,随机唤醒一个等待线程,可能唤醒一个不是该通知线程需要通知的线程
上下文切换
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值