当前位置: 首页 > java >正文

多线程(三)

上一期关于线程的执行,咱们说到线程是 “ 随机调度,抢占式执行 ”。所以我们对于线程之间执行的先后顺序是难以预知的。

例如咱们打篮球的时候,球场上的每一位运动员都是一个独立的 “ 执行流 ”,也可以认为是一个线程,进球就是咱们要执行的 “ 任务 ”,我们打球肯定不是说每个球员都想着抢球投球,要想赢得一场漂亮的比赛,咱们肯定是要执行战术的,每个人都有自己的位置,什么时候谁持球做什么,这都是很严谨的。只有多位运动员相互配合,按照一定顺序打出进攻防守,这样才能赢球。

线程是 “ 天生 ” 就这样互相抢占式执行的,但是我们难道就放任线程之间这样子随意执行不管嘛,当然不是了,有些时候在特定的场景下,咱们对实现线程的调度执行是有一定要求的。而这就引入了我们的 wait(等待) 和 notify(通知)。

(1)wait()/ wait(long timeout):让当前线程进入等待状态。

(2)notify()/ notifyAll():唤醒在当前对象上等待的线程 / 唤醒全部线程。

注意:wait 和 notify 、notifyAll 都是 Object 类的方法。

一 . wait(等待)

wait 要做的事:

(1)使当前执行代码的线程进行等待(将线程放到等待队列中)。

(2)释放当前的锁。

(3)满足一定条件时被唤醒,重新尝试获取这个锁。

注意:wait 一定要搭配 synchornized 使用,不然会直接抛出异常。

wait 结束等待的条件:

(1)其他线程调用该对象的 notify 方法。

(2)当使用 wait 的带参数版本时,等待超时自动唤醒。

(3)其他线程调用该线程的 interrupted 方法,会导致 wait 抛出 InterruptedException 异常。

咱们通过代码来看一下:

大家可以看出来,当我们使用无参数版本的 wait 时,若是我们不将其唤醒,那么这个线程就会一直等待下去,直到被唤醒为止。

wait 默认是 “死等  ”, 这里我们再使用 wait 的带参数版本,看一下是否会自动唤醒 wait 。

如图所示,经过咱们等待前后的时间差可以看出 wait 确实是等待了 3000 ms 还没有等待人来唤醒它,这时候它就不等了,线程直接继续执行。

 二 . notify(通知)

notify 要做的事:

(1)唤醒 wait 等待的线程。

(2)如果有多个线程等待,则由线程调度器随机唤醒其中的一个呈 wait 状态的线程。

(3)notify 同样要配合 synchornized 使用。

(4)在 notify()方法之后,当前线程不会立马释放该对象锁,而是要等待执行 notify()方法的线程将程序执行完,也就是退出同步代码块后才会释放对象锁。

大家通过下列代码看一下,wait 和 notify 配合 synchornized 使用:

notifyAll 跟 notify 的区别就在于:

(1)notify 是随机唤醒等待队列中的一个线程,其余还有的线程还得乖乖等着。那么我们还想要继续唤醒剩下的线程怎么办呢?这个时候有小伙伴就灵机一动了,我们多唤醒几次,多调用几次 notify 不就行了吗?NONONO,这样可不行,要想唤醒剩下的线程,我们得在各线程内部进行 wait 和 notify 的配合使用才行。

接下来我们来看这样一个示例,按照顺序打印 A、B、C :

在这里再一次强调,我们线程的执行是 “ 随机调度,抢占式执行 ”,所以这里打印A、B、C的顺序我们是无法预知的,如下图所示。

这个时候咱们就需要 wait 和 notify 配合使用,并且我们需要两把锁。

此时我们不管经过多少次试验,结果都是我们预期的 A、B、C 顺序。

注意:这里我们在线程 t1 中为什么要强制等待一个 10 ms 呢?如果我们不加这个强制等待就会发现,当我们执行代码的时候,只能打印出 A ,然后程序就不动了。这是为什么呢?这是因为大概率存在这样的情况:我们的 t1 先执行了打印和 notify ,执行的很快,然后我们的 t2 才执行 wait ,这就意味着 t1 的通知过早了,这样就会造成我们的 t2 一直等下去,因为属于它的通知已经错过了。

这就好比如一个悲伤的故事,在学生时代,有一对男孩女孩互生情愫,但都是暗恋,等到即将毕业前,女孩在送给男孩的一本书里偷偷夹了一封信,已示告白,但是粗心大意的男生并没有发现,直到多年后,男孩偶然间翻开这本书,这才发现这封已经泛黄的告白信,但此刻时光荏苒,物是人非事事休咯,这就是错失的遗憾。

这个故事是我在听老师讲课的时候,老师举的例子,但是很不幸的是,我也有着一段这样的经历,不是百分百一样,但也所差无几。

缘分就像一本书,翻得太快会错过,读的认真又流泪。所以各位,有时候冲动一点不是坏事,大家在该勇敢的时候还是要牢牢地抓出那个他 / 她,忘诸君不留遗憾。

(2)notifyAll 是一下将所有等待队列中的线程全部唤醒,但是,这些锁会重新竞争。notifyAll 很简单,咱们就不在这儿演示了。

 三 . wait 和 sleep 的区别 

理论上来说,wait 和 sleep 是没有任何可比性的,因为这两个概念虽然实现的作用大致相同,都可以让线程放弃执行一段时间,但是并不是一个性质的。

(1)wait 是用于线程之间通信的,sleep 是让线程阻塞一段时间。

(2)wait 需要搭配 synchornized 使用,sleep 不需要,直接通过 Thread 调用即可。

(3)wait 是 Object 的方法,sleep 是 Thread 的静态方法。

(4)sleep 与锁无关,不加锁,sleep 可以正常使用;加了锁,sleep 操作,不会释放锁,相当于在 sleep “ 休眠 ” 的这段时间内,它就是抱着锁睡得,那么其余线程都拿不到锁。

OKK,今天就说这么多了,这一期是专门讲解  wait 和 notify 的,大概就这些了。咱们下期再见吧,与诸君共勉!!!

http://www.xdnf.cn/news/6208.html

相关文章:

  • 团结引擎 1.5.0 发布,抖音小游戏平台即将开放、Shader Graph功能新增…引擎能力再提升!
  • 深入探索局域网技术:从理论到实战
  • 如何下载 Microsoft SQL Server Management Studio 2019
  • 最大子段和(就是之前总结线性dp思想)
  • 现代垃圾收集器
  • 公路水运安全员A证备考要点
  • 如何解决电脑蓝屏错误代码:Oxc0000098
  • OSS-承载数据的巨轮
  • 同设备访问php的多个接口会有先后等待问题
  • 基于 art 下的类加载机制,实现函数抽取壳
  • Java—接口和抽象类
  • WordPress 文章和页面:它们的区别是什么?
  • Pomelo知识框架
  • Python爬虫之品牌口碑数据抓取
  • 识别硬盘驱动器的接口类型,及其与计算机连接的方式
  • 碎片笔记|AI生成图像溯源方法源码复现经验(持续更新中……)
  • 解放双手的鼠标自动点击软件
  • R语言学习--Day02--实战经验反馈
  • 基于EFISH-SCB-RK3576/SAIL-RK3576的智慧路灯控制器技术方案
  • 高压开关/断路器机械特性试验的目的及设备
  • [python] python静态方法,类方法,实例方法实现及其区别
  • 【沉浸式求职学习day39】【双指针算法题】
  • 公链开发及其配套设施:钱包与区块链浏览器
  • 【Python】杂乱-[代码]Python 输出/打印列表(list)的方法
  • 三子棋设计
  • C#上位机RS485通信控制变频器
  • 3、ubantu系统docker常用命令
  • Centos 上安装Klish(clish)的编译和测试总结
  • NixOS 系统深度解析
  • Profibus DP主站转Modbus RTU/TCP网关接艾默生流量计与上位机通讯