线程通信之wait()-notify()

## 线程通信之wait()-notify()

线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。

例如,线程B可以等待线程A的一个信号,这个信号会通知线程B数据已经准备好了。

Object提供的wait()notify()

wait,notify,notifyAll是定义在Object类的实例方法,用于控制线程状态。

  • wait()方法 表示持有对象锁的线程A准备释放对象锁权限,释放CPU资源并进入等待。
  • notify() 表示持有对象锁的线程A准备释放对象锁权限,通知JVM唤醒某个竞争该对象锁的线程X。线程A synchronized 代码作用域结束后,线程X直接获得对象锁权限,其他竞争线程继续等待(即使线程X同步完毕,释放对象锁,其他竞争线程仍然等待,直至有新的notify() ,notifyAll()被调用)。
  • notifyAll() 表示持有对象锁的线程A准备释放对象锁权限,通知JVM唤醒所有竞争该对象锁的线程,线程A synchronized 代码作用域结束后,JVM通过算法将对象锁权限指派给某个线程X,所有被唤醒的线程不再等待。线程X synchronized 代码作用域结束后,之前所有被唤醒的线程都有可能获得该对象锁权限,这个由JVM算法决定。

通俗比喻:

有两个人A和X都要和一个女孩G约会(A线程和X线程都调用synchronized约会方法,锁对象G.class)。

A率先和G约会了(A线程进入synchronized方法),X只能等着。

A在约会途中被电话叫走,把G凉在那里(调用G.class.wait())。

这时候X发现G没人约会了,于是上场(X线程进入synchronized方法)。

等到X和G约会完成,X又打电话叫A回来继续约会(X线程调用G.class.notify()方法),A才回来继续约会直到完成。

notifyAll()就是不止A和X了,可能有更多的人要和G约会,等A走了之后某个人上场。

下面用一个案例来更好的了解这两个方法的使用和机制。

案例:控制台随机输入一个正整数n,通过两个线程分别打印n 1、n 2、n 3、n 4、n 5、n 6,要求线程交替打印(即一个奇数线程,一个偶数线程),n的初始指为-1。

先建一个奇偶数打印的类
public class ParityPrint {
	/** 初始化-1 */
    private int i = -1;
}
再分别创建奇数线程和偶数线程

  • 这里两个线程都用同一个锁ParityPrint.class,这样两个线程就不会同时打印了。
  • (parityPrint.i & 1) == 1判断奇偶用位运算,最后一位0是偶数,1是奇数。
  • i的初始值是-1,等到控制台输入才能继续,所以两个线程刚开始会交替运行到ParityPrint.class.wait()这里,然后两个线程会进入wait状态,全部释放锁。
跑一下

根据控制台打印可知两个线程都跑到wait(),但是没有再跑下去。

控制台输入,唤醒两个线程

这里main方法用notifyAll来唤醒了两个线程,结果符合预期。

这里我把i改成了volatile修饰,因为如果不修饰可能会出现一个问题:输入奇数,当偶数线程被唤醒将i改成了偶数,但是还没有刷新到主内存,这时候奇数线程获得锁,发现i还是还是奇数(主内存是偶数了,但是还没同步倒线程栈内存),又重新进入wait()方法,导致两个线程都进入wait状态。

思考
  1. main方法用notify()唤醒一个线程行不行?

    可以,但是如果输入一个奇数,又只唤醒了奇数线程,那奇数线程又回到wait状态。

  2. 为什么日志里主线程唤醒所有线程后,奇数线程获得了锁,但是又回到了等待中?

    notifyAll后奇数线程率先获得锁,执行while条件发现目前输入的是奇数,所以又重新wait放弃了锁。

  3. parityPrint.i < 0 || (parityPrint.i & 1) == 0if判断行不行?

    不行,因为当输入奇数,主线程唤醒两个线程,如果奇数线程优先获得锁,就会直接往下打印一个偶数。

代码可以在Github上找到。

更多内容
本文来自chentiefeng的博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值