java:一个 wait(timeout) 引出的你可能对锁的误解

本贴最后更新于 2862 天前,其中的信息可能已经天翻地覆

在很多讲 wait(long timeout)的例子,都会用下面类似的代码:

public class RunA implements Runnable {

	private Object lock;

	public RunA(Object lock) {
		this.lock = lock;
	}

  @Override
   public void run() {

	  synchronized (lock){
		  try  {
			   System.out.println("A begin");
			  // lock.wait(); // 永远等待着,不会执行下去
			   lock.wait(2000);// 等待了2秒之后,继续执行下去
			   System.out.println("A end");
		  } catch (InterruptedException e) {
			  e.printStackTrace();
		  }
	  }
  }
}

举这样的例子显然是没有任何意义的,在这里用 wait(2000)和 sleep(2000) 有什么区别呢?

wait 和 sleep 显然是有很大的区别,但区别不只是 wait 会把 lock 释放掉,然我们引入一个新的搅和线程 B

public class RunB implements Runnable {

	private Object lock;

	public RunB(Object lock) {
		this.lock = lock;
	}

	@Override
  public void run() {

		synchronized (lock) {
			System.out.println("b come");
			while (true) {
			}
		}
	}
}

B 仅仅是握住锁,然后永远不释放,然后回到我们的主舞台 main 函数:

public static void main(String[] args) throws InterruptedException {

	Object lock = new Object();
	Thread threadA = new Thread(new RunA(lock));
	threadA.start();
	threadA.wait();

	Thread.sleep(1000);

	Thread threadB = new Thread(new RunB(lock));
	threadB.start();

}

然后再 run 一下,发现 A end ying 永远不会打印了,咦,为啥 wait(2000)之后没有被唤醒执行下去了呢?

仔细想想 A 显示获得了锁,然后 wait(2000)交出了锁,然后 B 拿到了锁,这个时候过了 2 秒,A 确实是被唤醒了,但很可惜 A 永远也不会得到锁了,对于临界区永远只能有一个线程在执行,不可能出现两个临界区同时在执行代码的可能,所以被唤醒之后,还需要去争抢锁,并不是唤醒了就能继续执行代码的

一个线程被唤醒可能有一下四种情况

  1. 其它的线程调用 obj.notify(),且当前线程 T,正好是被选中唤醒的。

  2. 其它的线程调用 obj.notifyAll()。

  3. 其它线程中断 T。

  4. 指定的等待时间(timeout)超时,(时间精度会有些误差)。

但我想说一下一个完整的过程是,唤醒之后需要去抢到临界区的锁,才能真正把代码执行下去,光有唤醒是不够的

大多数时候我们忽略唤醒之后需要去抢到临界区的锁,是因为 notify 用的多的关系,触发 notify 的线程必然有锁,只会唤醒一个线程,所以被唤醒的线程必然得到锁!于是大家就会产生一个被唤醒一定能执行点的错觉

而我觉得很多文章没有指明这一点,然当你意识到了这个之后,对 notifyAll 就也不会有一起全部唤醒执行的想当然理解了,notifyAll 只是让大家都去抢临界区,所有的 wait notify,都是为了保护临界区永远只能有一个在执行,分析问题还是从源头入手,见笑。。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注
  • 多线程并发
    1 引用 • 5 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • tmedivh

    6666666666666666666666666666666666

  • 其他回帖
  • liuguichuan

    那多核处理器可以同时运行多个线程是有多个临界区吗?

    1 回复
  • smart

    这个应该跟多核单核没有关系,他这个例子是在说同一时刻只能有一个线程拥有这把锁。
    一个线程获取到了锁之后放弃掉,被一个 demon 线程获取到,那之前那个线程始终获取不到这把锁了,所以一直等待了。
    另外你这个 ```
    threadA.wait();

    @mainlove 
    
    1 回复
  • eddy

    6666666

  • 查看全部回帖

推荐标签 标签

  • abitmean

    有点意思就行了

    30 关注
  • CongSec

    本标签主要用于分享网络空间安全专业的学习笔记

    1 引用 • 1 回帖 • 17 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 614 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 726 关注
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    23 引用 • 32 回帖 • 1 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖
  • 域名

    域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。

    43 引用 • 208 回帖
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖
  • 又拍云

    又拍云是国内领先的 CDN 服务提供商,国家工信部认证通过的“可信云”,乌云众测平台认证的“安全云”,为移动时代的创业者提供新一代的 CDN 加速服务。

    21 引用 • 37 回帖 • 549 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    132 引用 • 1114 回帖 • 124 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖 • 1 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖 • 1 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 30 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    343 引用 • 723 回帖
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    23126 引用 • 93121 回帖 • 2 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 72 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 4 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    132 引用 • 795 回帖
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 1 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    209 引用 • 358 回帖
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    410 引用 • 1246 回帖 • 588 关注
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    123 引用 • 74 回帖 • 1 关注