JAVAEE-5

wait和notify分别对应着等待和通知机制

它们和join的用途类似,使用wait是基于线程是随机调度的这一底层

我们这里引入wait和notify,是为了能够从应用层面上,干预到多个不同线程代码的执行顺序.

这里说的干预,不是影响系统的线程调整策略(内核仍然是无序随机调度)

wait和notify

join,我们这样做,相当于是在应用程序代码中,让后执行的线程主动放弃被调度的机会,就可以让先执行的线程,先把对应的代码执行完了

例子:饥饿线程

举一个ATM机的例子,假如现在有ABCD4个人,每个人都对应一个线程,A现在要取钱,BCD现在要存钱.现在A进入到ATM机里面,进行取钱操作,可是他发现取钱时,ATM里面没有钱,那么这个时候A发现没钱,就要出来,但是A没取到钱,所以A又要进去取钱,一直都是A在操作ATM,其他人操作不了ATM,也就是A一直在执行线程调度,其他线程进入锁状态,但是当A脱离锁之后,发现A的任务还没有完成,又接着抢夺锁,可能多次都是他抢到锁了,这样其他线程就无法执行,我们称这种现象为饥饿线程.

针对这种情况,我们使用wait,notify,当ATM里面没钱的时候,我们让A使用WAIT,进入WAITING状态,在wait方法调用处,会进入WAITING状态此时A线程会自动释放锁,并且放弃参与到锁竞争当中去,那么此时其他线程往ATM里面去存钱,当存到一定数额时,调用notify方法,此时,A线程就会就会被唤醒,又参与到锁的竞争中去.

伪代码:

while(true){

synchronize()

{if(ATM有钱)

取钱操作;

break;

else{

wait();//如果没钱,就主动进入阻塞状态

}

}

wait的作用:

1.释放锁

2.进入阻塞等待

3.当其他线程调用notify的时候,wait解除阻塞,并重新获取到锁

join与wait的区别:

join是等待另一个线程执行完之后,才继续执行

wait是等待notify通知;

    Object locker1=new Object();
        locker1.wait();

直接使用wait方法会进入报错,我们要在对应的锁里面使用才可以.

调用wait方法要让线程解锁,就必须和synchronized的锁对象object是一致的,

synchronized(object){

object.wait();}

并且wait在唤醒之后,依然要重新获取到原来的object锁

其他线程在调用notify时候,也同样需要相同的锁对象.

  Object locker1=new Object();
      synchronized (locker1){
          System.out.println("wait之前");
          locker1.wait();
          System.out.println("wait之后");

可以观测到,改线程在调用wait方法之后,进入到阻塞WAITING状态

   try {
              locker1.wait();
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }

wait方法和join,sleep一样,也是有可能会被interrupt提前唤醒的

 Object object=new Object();
        Thread t1=new Thread(()->{
            synchronized(object){
                System.out.println("wait之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait之后");

            }
        });
        Thread t2=new Thread(()->{
            try {
                  Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            synchronized (object){
                
                System.out.println("notify之前");
                object.notify();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("notify之后");
            }
        });
        t1.start();
        t2.start();

执行结果,可以看到,在notify之后,t2释放锁之后,t1立马获取到锁,在之前wait调用处,接着往下执行.

上述代码执行逻辑:

1)t1执行之后,就会立即先拿到锁,并且在wait之前进行打印,之后进入wait方法,就会释放锁,并且进入阻塞状态.

2)t2执行起来后,会先执行sleep(1000),目的是确保t1能够先拿到锁.

3)t2sleep结束之后,因为t1是WIATING状态,不会进入锁竞争,因此没有线程和t2争夺锁,那么t2就能顺利拿到锁,进行打印,执行notify操作,此时t1被唤醒,但是此时是由WAITING状态进入到BLOCKED猪状态,会一直尝试获取锁

4)由于此时t2还没有释放锁,所以t1并不能立刻马上拿到锁,此时的阻塞是由于锁竞争引起的.得等到t2执行完释放锁之后,t1才能拿到锁,

5)t2执行完notify,释放锁,t2执行完毕,t1的wait就可以获取到锁了,继续执行打印

wait两个方法:

1)不带參数的wait,如果没有人进行通知,他就会一直等,俗称死等

2)带參数的,会设置一个等的最长时间.如果超过这个时间还没有notify进行通知,那么他就不会再等了

在实际的开发当中,使用wait带參数的情况为多,在商业程序上,我们要考虑"鲁棒性"_容错能力,就是"你对他越粗鲁,他表现的越棒",即使出现一些差错,也不会有太大影响,能够自动恢复.

2)wait和notify彼此之间,是通过object对象联系起来的

object1.wait()

object2.wait()

此时是无法唤醒的,必须是两个对象一致才能唤醒.,此外,一次只能唤醒一个等待的线程

3)notifyAll 

唤醒这个对象上的所有等待线程

假设有很多个线程,都使用同一个对象wait

针对这个对象进行notifyAll,此时就会全都唤醒~~

但是,要注意,这些线程在wait返回的时候,要重新获取锁,就会因为锁竞争,使这些线程实际上是一个一个串行执行的(谁先拿到锁,后拿到,也是不确定的),

相比之下,notify更好控制,notifyAll全都唤醒之后,不太好控制

wait和sleep之间的区别:

wait提供了一个带有超时时间的版本

sleep也是能指定时间~~

都是时间到,就继续执行,解除阻塞了.

wait和sleep都可以被提前唤醒

wait通过notify进行唤醒

sleep通过interrupt唤醒

使用wait的主要目标,一定是不知道等多少时间的前提下使用的,所谓的超时时间是兜底的.

使用sleep,一定是知道需要等待多长时间的前提下使用的,虽然能够提前唤醒,但是通过异常唤醒,这个操作不应该作为正常的业务流程(通过异常唤醒,说明程序是出现了一些特殊的情况了);

sleep提前唤醒,是通过异常的方式,正常的业务流程不应该依赖于异常处理,异常处理认为是一些不就措施.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值