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

【JAVA】synchronized 关键字的底层实现涉及得三个队列

synchronized 关键字的底层实现确实涉及三个主要的队列:cxq(Contention Queue)、EntryList 和 waitSet。这三个队列分别用于管理不同类型的等待线程。下面是每个队列的详细解释及其作用:

1. cxq(Contention Queue)

定义:cxq 是一个竞争队列,用于存储那些未能立即获得锁的线程。
行为:当一个线程试图获取一个已经被其他线程持有的锁时,该线程会被放入 cxq 中等待。cxq 是一个基于链表的数据结构,线程按照先进先出(FIFO)的原则排队。当锁被释放时,cxq 中的一个线程会被移到 EntryList 中,准备重新竞争锁。

2. EntryList

定义:EntryList 是一个等待队列,用于存储那些已经准备好竞争锁的线程。
行为:当 cxq 中的线程被移到 EntryList 中时,它们会尝试获取锁。
EntryList 也是一个基于链表的数据结构,线程按照先进先出(FIFO)的原则排队。当锁被释放时,EntryList 中的一个线程会被选中并获得锁。

3. waitSet

定义:waitSet 是一个条件等待队列,用于存储那些调用 wait 方法后被阻塞的线程。
行为
当一个线程在临界区内调用 wait 方法时,它会释放锁并被放入 waitSet 中等待。
当其他线程调用 notify 方法时,waitSet 中的一个线程会被唤醒并重新竞争锁。
当其他线程调用 notifyAll 方法时,waitSet 中的所有线程都会被唤醒并重新竞争锁。

详细流程

获取锁:
线程 A 尝试进入由 synchronized 保护的代码块。
如果锁可用,线程 A 获得锁并进入临界区。
如果锁不可用,线程 A 被放入 cxq 中等待。
进入临界区:
线程 A 获得锁并进入临界区,执行受保护的代码。
调用 wait 方法:
线程 A 在临界区内调用 wait 方法。
线程 A 释放锁并被放入 waitSet 中等待。
其他线程(例如线程 B)可以竞争并获得锁。
释放锁:
当线程 B 完成临界区的执行或调用 notify/notifyAll 方法时,它会释放锁。释放锁后,cxq 中的一个线程(例如线程 C)会被移到 EntryList 中,准备重新竞争锁。
唤醒等待线程:
notify 方法:唤醒 waitSet 中的一个线程,使其重新竞争锁。
notifyAll 方法:唤醒 waitSet 中的所有线程,它们都会尝试获取锁。
释放锁后的具体行为
释放锁后:
优先从 cxq 中取出一个线程,将其移到 EntryList 中。
EntryList 中的线程会尝试获取锁。
如果 EntryList 为空,才会考虑 waitSet 中的线程。

public class SynchronizedExample {private final Object lock = new Object();public void methodA() {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " entered methodA");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " exiting methodA");}}public void methodB() {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " entered methodB");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " exiting methodB");}}public void methodC() {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " entered methodC");try {Thread.sleep(1000);lock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " exiting methodC");}}public void methodD() {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " entered methodD");try {Thread.sleep(1000);lock.notifyAll();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " exiting methodD");}}public static void main(String[] args) {SynchronizedExample example = new SynchronizedExample();Thread thread1 = new Thread(() -> example.methodA(), "Thread1");Thread thread2 = new Thread(() -> example.methodB(), "Thread2");Thread thread3 = new Thread(() -> example.methodC(), "Thread3");Thread thread4 = new Thread(() -> example.methodC(), "Thread4");Thread thread5 = new Thread(() -> example.methodD(), "Thread5");thread1.start();thread2.start();thread3.start();thread4.start();thread5.start();}
}

解释

获取锁:
Thread1 和 Thread2 同时尝试进入 methodA 和 methodB,这两个方法都使用同一个 lock 对象。
只有一个线程可以获得锁并进入临界区,另一个线程会被放入 cxq 中等待。
进入临界区:
获得锁的线程进入临界区,执行 System.out.println 和 Thread.sleep 语句。
调用 wait 方法:
Thread3 和 Thread4 在 methodC 中调用 wait 方法,释放锁并被放入 waitSet 中等待。其他线程(例如 Thread5)可以竞争并获得锁。
释放锁:
当 Thread5 在 methodD 中调用 notifyAll 方法时,它会释放锁。
释放锁后,cxq 中的一个线程(例如 Thread1 或 Thread2)会被移到 EntryList 中,准备重新竞争锁。
waitSet 中的所有线程(例如 Thread3 和 Thread4)也会被唤醒并重新竞争锁。
输出结果:
由于 synchronized 的互斥性,输出结果会显示多个线程依次进入和退出临界区。
总结
cxq:竞争队列,用于存储那些未能立即获得锁的线程。
EntryList:等待队列,用于存储那些已经准备好竞争锁的线程。
waitSet:条件等待队列,用于存储那些调用 wait 方法后被阻塞的线程。
通过这三个队列,synchronized 能够在高并发环境下有效地管理线程的竞争和等待,确保线程同步的公平性和效率

相关文章:

  • Python知识点:如何使用Python进行物联网数据处理
  • JavaScript的条件语句
  • hive分区详细教程
  • 基于flask常见trick——unicode进制编码绕过
  • 【rabbitmq-server】安装使用介绍
  • Mac写入U盘文件如何跨平台使用 Mac电脑怎么把U盘文件传送到电脑 mac怎么用u盘拷贝文件
  • MMD模型一键完美导入UE5-VRM4U插件方案(一)
  • 国产sql工具何时才能出头?
  • 搜维尔科技:使用Xsens动作捕捉系统和ai训练人形机器人模仿人类运动,执行复杂任务
  • Redis:事务
  • C语言进阶【6】---结构体【1】(结构体的本质你不想了解吗?)
  • Windows电脑使用VNC远程桌面本地局域网内无公网IP树莓派5
  • Redis 性能优化的高频面试题及答案
  • Xcode 16 Pod init 报错
  • Linux服务器安装Anaconda环境
  • 2017前端实习生面试总结
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • java中具有继承关系的类及其对象初始化顺序
  • js ES6 求数组的交集,并集,还有差集
  • laravel 用artisan创建自己的模板
  • Leetcode 27 Remove Element
  • oldjun 检测网站的经验
  • PAT A1092
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • vue--为什么data属性必须是一个函数
  • 读懂package.json -- 依赖管理
  • 使用权重正则化较少模型过拟合
  • 学习HTTP相关知识笔记
  • 栈实现走出迷宫(C++)
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • # 数论-逆元
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • $.ajax()参数及用法
  • (42)STM32——LCD显示屏实验笔记
  • (5)STL算法之复制
  • (Java入门)抽象类,接口,内部类
  • (poj1.3.2)1791(构造法模拟)
  • (ros//EnvironmentVariables)ros环境变量
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (十六)Flask之蓝图
  • .cn根服务器被攻击之后
  • .NET : 在VS2008中计算代码度量值
  • .net 7和core版 SignalR
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .vimrc 配置项
  • /usr/lib/mysql/plugin权限_给数据库增加密码策略遇到的权限问题
  • ??Nginx实现会话保持_Nginx会话保持与Redis的结合_Nginx实现四层负载均衡
  • @Autowired 与@Resource的区别
  • @property @synthesize @dynamic 及相关属性作用探究
  • [ CTF ] WriteUp-2022年春秋杯网络安全联赛-冬季赛
  • [ MSF使用实例 ] 利用永恒之蓝(MS17-010)漏洞导致windows靶机蓝屏并获取靶机权限
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务