用synchronize实现等待-

java语言里,等待-通知机制可以有多种实现方式,比如java语言内置的synchronize配合 wait,notify,notifyAll 这个三个方法就可以实现
下图中:左边有一个等待队列,同一时刻,只允许一个线程进入synchronize临界区,当一个线程进入临界区之后,其他的线程只能在左边等待队列里等待。
这个等待队列和互斥锁是一对一的关系,每个互斥锁都有自己独立的等待队列

在这里插入图片描述

在并发程序中,当一个线程进去临界区之后,由于某些条件不满足,需要进入等待状态,此时可以使用wait方法,如上图,当调用wait方法后,当前线程就会被阻塞,并且会进入到右边的等待队列,这个等待队列也是互斥锁的等待队列,线程在进入等列队列的同时,会释放持有的锁,释放锁之后其他的线程就有机会获得锁进入临界区了

当条件满足的时候,我们怎么通知线程线程呢?通过notify 或者notifyAll方法,当条件满足时,调用notifyAll会唤醒通知右边等待队列中的所有线程,告诉他们条件曾经满足过。为什么说是曾经满足过呢,因为notifyAll只能保证在通知的时间点,条件是满足的。而被通知线程的执行点和通知的时间点基本上是不会重合,所以线程执行的时候,很可能条件已经不满足了(其他线程插队)。这个判断条件的循环一定要使用while,因为使用while唤醒线程后还会执行一下while里面的判断操作,还有内通知的线程想要重新执行,依然需要重新获得互斥锁(锁在wait的的时候已经释放了)

wait ,notify ,notifyAll 方法操作的是等待队列是互斥锁的等待队列,如果synchronized 锁定的对象是this,那么对应的一定是this.wait(),this.notify(),this.notiftAll(),而且wait方法能执行的前提是已经获得了该对象的互斥锁

  while(条件不满足) {
    wait();
  }

当wait返回时,有可能条件已经发生了变化,曾经条件满足,有可能条件已经发生了变化,所以需要重新校验条件。

在通知唤醒线程的时候尽量使用notifyAll() ,notify()是会随机唤醒等待队列中的一个线程,而notifyAll()会唤醒所以的线程。从感觉上来讲notify()更好一些,因为即便是通知所有线程但也只有一个线程可以进去临界区。但是那个所谓的感觉下往往蕴藏着风险,notify()的风险在于导致某些线程可能永远不会被通知到

假设我们有资源A,BC,D, 线程1申请到了AD, 线程2申请到了CD, 此时线程3申请AB,会进入到等待队列,线程4申请CD也会进入等待队列,假设线程1归还了资源AB,如果使用notify(),可能被唤醒的是线程4,而线程4申请的CD,所有此时线程4还是会继续等待,而真正该被唤醒的线程3就再也没有机会被唤醒了

wait 和sleep的区别

wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。
sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值