Java中常见的等待唤醒机制及实践

news2024/12/23 10:44:56

JDK自带的等待唤醒机制

在Java中,有一个JDK维度的等待唤醒机制。Object类的wait和notify,notifyAll
需要在synchronized同步代码块内并且对象必须获取到锁才能调用。否则会抛IllegalMonitorStateException异常。
当线程在尝试获取锁时失败,会被封装成节点(Node)并加入到等待队列中,进行同步等待。
等待队列底层是一条维护着头尾指针的Node节点链表。

					Object obj=new Object();
					synchronized (obj){
						System.out.println(Thread.currentThread().getId()+"开始");
						obj.wait(3000);
						System.out.println(Thread.currentThread().getId()+"结束");
					}

如上,obj是synchronized代码块的锁对象,在同步代码块内的线程必定获取到了obj对象的对象锁,因而可以调用obj的等待唤醒方法。
调用obj.wati(),当前线程会释放obj对象的锁,并进入obj对象的等待队列,等待被其他线程调用obj对象的notify或notifyAll唤醒。

public class NotifyTest {
	public static void main(String[] args) {
		Object obj=new Object();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronized (obj){
					System.out.println(Thread.currentThread().getId()+"开始"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					obj.wait(5000);
					System.out.println(Thread.currentThread().getId()+"结束"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronized (obj) {
						System.out.println(Thread.currentThread().getId() + "开始了"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
						Thread.sleep(1000);
						obj.notifyAll();
						Thread.sleep(1000);
						System.out.println(Thread.currentThread().getId() + "结束了"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();

	}
}

在这里插入图片描述

如上图,线程12先获取锁,并执行打印,然后调用wait方法释放obj的锁并进入obj等待队列,线程13也启动并执行打印,然后调用notifyAll方法唤醒线程12,但是调用notifyAll方法只是会唤醒线程12,并不会立刻释放锁(线程13唤醒线程12之后睡了1秒),所以是先执行完线程13的打印,释放obj锁,然后线程12再获取锁并打印后续。

Lock的等待唤醒机制

条件队列的前置条件是获取到Lock接口和Condition接口
需要创建Lock对象,然后调用newCondition方法创建Condition对象。这个Condition对象就是一个条件队列,底层是一条维护着头尾指针的链表。条件队列的具体条件逻辑需要用业务代码去实现。
一个Lock对象可以创建多个Condition对象,也就是说一个Lock对象可以有多个条件队列,并且不同条件队列之间互不影响。因而可以实现更细维度的线程睡眠唤醒机制。
如ReentrantLock类的newCondition方法每次都会返回一个新的Condition对象,可以用以区分不同的等待条件。

public class NotifyTest {
	public static void main(String[] args) {
		Lock lock = new ReentrantLock();
		Condition condition1 = lock.newCondition();
		Condition condition2 = lock.newCondition();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getId() + "条件1开始" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					lock.lock();
					System.out.println(Thread.currentThread().getId() + "条件1获取到锁" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					condition1.await();

				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println(Thread.currentThread().getId() + "条件1结束" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					lock.unlock();
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getId() + "条件2开始" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					lock.lock();
					System.out.println(Thread.currentThread().getId() + "条件2获取到锁" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					condition2.await();

				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println(Thread.currentThread().getId() + "条件2结束" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					lock.unlock();
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getId() + "开始" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					//先睡一秒,确保上面启动的两个线程先后获取锁并阻塞
					Thread.sleep(1000);
					lock.lock();
					System.out.println(Thread.currentThread().getId() + "获取到锁" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					condition2.signal();
					Thread.sleep(1000);
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					System.out.println(Thread.currentThread().getId() + "结束" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
					lock.unlock();
				}
			}
		}).start();

	}
}

在这里插入图片描述

如上图,三个线程先后启动,唤醒的线程先睡眠一秒,防止和条件1条件2线程抢锁。条件1和条件2按照先后顺序拿到锁,分别调用不同的条件对象阻塞等待条件。接着唤醒线程睡眠一秒之后唤醒,并获取锁,打印,然后唤醒等待条件2的线程,执行完毕释放锁。锁释放之后,条件2的线程检测到锁可用,就获取锁,并继续执行。而条件1的线程因为没有被唤醒,一直在阻塞等待唤醒条件。

可以通过业务代码去限制不同的线程所需的唤醒条件,更精细维度的锁控制。

结论

  • JDK自带的等待唤醒机制实现简单,所有等待的线程都阻塞在锁对象上,所有等待的线程都参与获取锁。

  • Lock实现的等待唤醒机制可以实现条件等待,也就是等待获取锁的线程,需要先达到某种条件被唤醒(从条件队列移到等待队列)才能参与获取锁,这样可以实现更加精细化的线程控制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2197908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mybatis-plus做了什么

Mybatis-plus做了什么 Mybatis回顾以前的方案Mybatis-plus 合集总览:Mybatis框架梳理 聊一下mybatis-plus。你是否有过疑问,Mybatis-plus中BaseMapper方法对应的SQL在哪里?它为啥会被越来越多人接受。在Mybatis已经足够灵活的情况下&…

《强烈推荐一个强大的书签管理工具》

在信息爆炸的时代,我们每天都会浏览大量的网页,收藏各种各样的书签。然而,随着书签数量的增加,管理起来也变得越来越困难。这时,一个强大的书签管理工具就显得尤为重要。今天,我要向大家推荐一款备受好评的…

EtherCAT学习笔记

文章目录 前言一、EtherCAT介绍二、EtherCA系统组成2.1 ESC(EtherCAT从站控制器)2.2 从站控制微处理器2.3 物理层器件2.4 其它应用层器件 三、EtherCAT数据帧结构3.1 寻址方式3.2 时钟3.3 通信模式 四、状态机和通信初始化五、应用层协议六、ESC概述6.1 EtherCAT从站控制芯片6.…

基于SpringBoot+Vue+MySQL的美食信息推荐系统

系统展示 用户前台界面 管理员后台界面 系统背景 在数字化时代,随着人们对美食文化的热爱与追求不断增长,美食信息推荐系统成为了连接食客与美食之间的重要桥梁。面对海量的美食信息,用户往往难以快速找到符合个人口味和需求的美食。因此&…

Java-数据结构-Lambda表达式 (✪ω✪)

文本目录: ❄️一、背景: ➷ 1、Lambda表达式的语法: ➷ 2、函数式接口: ❄️二、Lambda表达式的基本使用: ➷ 1、语法精简: ❄️三、变量捕获: ❄️四、Lambda在集合中的使用: …

Chromium 中js navigator对象c++实现分析

一、Navigator 对象 Navigator 对象包含有关浏览器的信息。 前端测试例子&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>接口测试</title> </head> <body><div id"example&q…

爱心表达公式

脚本公式 local r (math.sin(angle) * math.sqrt(math.abs(math.cos(angle)))) / (math.sin(angle) 1.4) - 2 * math.sin(angle) 2

IOT-Tree连接西门子PLC S7 200 Smart竟然如此简单

最近一个项目需要把用户现场控制柜接入到云端&#xff0c;控制柜使用西门子PLC Smart 200 SR40型号&#xff0c;已经运行多年&#xff0c;PLC通过以太网接口对接一个触摸屏。 按照我以往的经验&#xff0c;觉得触摸屏以太网接口已经被占用&#xff0c;那么只能通过剩余的RS485…

通过一个实际的例子,介绍 Java 的自动装箱与拆箱机制

Java 中 1000 1000 返回 false&#xff0c;但 100 100 返回 true&#xff0c;这一现象背后隐藏了 Java 对于对象和基本类型的内存管理机制。为了理解这个现象&#xff0c;我们需要从 Java 的自动装箱与拆箱机制、对象引用和数值缓存策略等角度深入探讨。让我们一步一步通过 J…

电脑怎么卸载软件?学会这6个卸载软件技巧就够了(精选)

电脑怎么卸载软件&#xff1f;在日常的办公生活中&#xff0c;我们需要下载一些工具来辅助工作&#xff0c;当不需要这些工具的时候&#xff0c;我们就需要卸载这些软件了。很多小伙伴表示卸载软件卸载不干净&#xff0c;还是回残留一些文件&#xff0c;或者是卸载不了&#xf…

Verilog开源项目——百兆以太网交换机(九)表项管理模块设计

Verilog开源项目——百兆以太网交换机&#xff08;九&#xff09;表项管理模块设计 &#x1f508;声明&#xff1a;未经作者允许&#xff0c;禁止转载 &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN主页 &#x1f511;全新原创以太网交换机项目&#xff0c;Blog内容将聚焦整…

新型僵尸网络针对 100 个国家发起 30 万次 DDoS 攻击

近日&#xff0c;网络安全研究人员发现了一个名为 Gorilla&#xff08;又名 GorillaBot&#xff09;的新僵尸网络恶意软件家族&#xff0c;它是已泄露的 Mirai 僵尸网络源代码的变种。 网络安全公司 NSFOCUS 在上个月发现了这一活动&#xff0c;并称该僵尸网络在今年 9 月 4 日…

【Java 循环控制实例详解【While do... while】】

Java 循环控制详解【While & do… while】 在 Java 中&#xff0c;循环控制是程序设计中非常重要的部分&#xff0c;主要包括 while 循环和 do...while 循环。本文将详细介绍这两种循环的基本语法、执行流程及相关示例。 1. while 循环控制 基本语法 循环变量初始化; wh…

在uniapp中实现长按聊天对话框可以弹出对话框然后可以删除该条对话,单击可以进入该条对话框的对话页面

效果展示 效果描述 长按【大于1s】某一条对话框会弹出一个对话框&#xff0c;点击确定按钮就可以将当前对话框从列表中进行删除&#xff0c;如果点击取消则不做额外操作。 如果只是点击了一下&#xff0c;时间【小于1s】的情况下会直接引入到与该用户的对话框详情页面。 代码…

ai绘画变现方式全解析,教你如何通过AI绘画赚钱

*AI绘画变现方式全解析&#xff0c;教你如何通过AI绘画赚钱* *为什么选择AI绘画&#xff1f;* 你是否曾经梦想过成为一名画家&#xff0c;但现实却让你无从下手? 或者你已经是一位艺术家&#xff0c;但苦于作品没能带来足够的收入&#xff1f;随着AI技术的飞速发展&#xff…

IDEA没有代码自动提示问题的解决

一、问题描述 如图&#xff0c;博主输入new Hash 没有提示出HashMap之类的api 原因在于&#xff1a;不小心打开了idea的省电模式&#xff0c;在这个模式下&#xff0c;idea是不会为我们提供自动提示的 二、问题解决 在File中找到 Power Save Mode选项&#xff0c;将它关闭即…

【西电电路实验】1. 仪器的使用(电子线路 III 电院)

文章目录 前言一、实验原理二、实验过程1. [高频信号源DSG3030 数据手册](https://www.rigol.com/Images/DSG3000_DataSheet_CN_tcm4-3558.pdf)2. [信号源 SDS 2320X 数据手册](https://www.siglent.com/u_file/download/24_05_29/SDS2000X%20HD_Datasheet_CN02A.pdf#:~:textSD…

MySQL多表查询:标量子查询

先看我的emp表结构 emp表 子查询基本语法 select * from t1 where column1 (select column1 from t2);例子1&#xff1a;查询"销售部" 的所有员工信息 这个可以先拆解为两个 a.查询"销售部"的部门ID select id from dept where name 销售部; b. 根…

2024重生之回溯数据结构与算法系列学习(11)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】

欢迎各位彦祖与热巴畅游本人专栏与博客 你的三连是我最大的动力 以下图片仅代表专栏特色 [点击箭头指向的专栏名即可闪现] 专栏跑道一 ➡️ MYSQL REDIS Advance operation 专栏跑道二➡️ 24 Network Security -LJS ​ ​ ​ 专栏跑道三 ➡️HCIP&#xff1b;H3C-SE;CCIP——…

算法:724.寻找数组的中心下标

题目 链接&#xff1a;leetcode链接 思路分析&#xff08;前缀和&#xff09; 根据题意&#xff0c;我们可以将数组看成三个部分 [left] [mid] [right] 我们只需要[left]区间内的元素和等于[right]区间的元素和即可&#xff0c;此时mid就是中心下标 那么我们可以借助前缀和思…