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

JDK Synchronizers 设计及工作原理与教程

针对 JDK 同步器(Synchronizers),从设计思想核心工作原理分类与使用教程三个维度,为你构建一份系统性的技术指南。


一、 总体设计思想:AQS 框架

JDK 中几乎所有的同步器(ReentrantLockSemaphoreCountDownLatchCyclicBarrier等)底层都基于AbstractQueuedSynchronizer(AQS)构建。理解 AQS 就等于理解了 JDK 同步器的半壁江山。

AQS 核心三要素:

  1. 状态变量(state):一个volatile int state,用于表示共享资源的状态。例如:

    • ReentrantLockstate=0表示未锁定,>0表示重入次数。

    • Semaphorestate表示剩余许可数。

  2. CLH 队列(双向链表):一个先进先出(FIFO)的等待队列,用于存放争抢资源失败而被阻塞的线程。

  3. 两种资源共享模式

    • 独占模式(Exclusive):同一时刻只有一个线程能持有资源(如ReentrantLock)。

    • 共享模式(Shared):多个线程可同时持有资源(如SemaphoreCountDownLatch)。

AQS 设计精髓(模板方法模式):
AQS 不直接实现同步逻辑,而是定义了获取/释放资源的骨架流程(如acquire()release()),并将具体如何检查状态、修改状态留给子类实现(tryAcquiretryRelease)。

二、 核心工作原理(以 ReentrantLock 为例)

我们以非公平锁lock()unlock()为例,拆解 AQS 的运行机制:

  1. 加锁流程(lock)

    • 线程调用lock(),AQS 调用子类的tryAcquire()

    • 检查state

      • state == 0,通过 CAS 将state置为 1,并设置当前线程为独占所有者 →加锁成功

      • state > 0且当前线程就是所有者,state++(重入)→加锁成功

      • 否则 →加锁失败,将该线程封装成 Node 节点,加入 CLH 队列尾部。

    • 入队后的线程执行park()阻塞,等待前驱节点唤醒。

  2. 解锁流程(unlock)

    • 调用unlock(),调用子类的tryRelease(),将state减 1。

    • state减为 0,清空独占所有者。

    • 唤醒 CLH 队列中的下一个有效节点(unpark())。

    • 被唤醒的线程重新尝试tryAcquire()

关键优化:非公平锁在尝试tryAcquire前,会直接先进行一次 CAS 抢锁(插队),提升吞吐量但可能导致饥饿。

三、 常用同步器分类与工作机制(速查表)

同步器模式核心作用状态(state)含义
ReentrantLock独占可重入互斥锁,支持公平/非公平0=未锁,n=重入次数
Semaphore共享信号量,控制并发线程数剩余许可证数量
CountDownLatch共享门闩,等待多个任务完成剩余未完成的计数
CyclicBarrier独占(内部用 ReentrantLock)循环栅栏,等待所有线程到达屏障点基于Generation进行重置,不同于 AQS state
ReentrantReadWriteLock混合读写分离,读共享、写独占高16位读锁,低16位写锁

四、 使用教程与最佳实践(含代码示例)

1. ReentrantLock(替代 synchronized)
// 推荐用法:务必在 finally 中释放 private final ReentrantLock lock = new ReentrantLock(); public void doSomething() { lock.lock(); try { // 业务逻辑(支持 lockInterruptibly、tryLock 等高级特性) } finally { lock.unlock(); } }
2. Semaphore(限流 / 连接池控制)
// 模拟允许最多 3 个并发请求 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 10; i++) { new Thread(() -> { try { semaphore.acquire(); // 获取许可,阻塞直到有可用 System.out.println(Thread.currentThread().getName() + " 执行"); Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); // 释放许可 } }).start(); }
3. CountDownLatch(主线程等待子任务完成)
CountDownLatch latch = new CountDownLatch(3); // 启动 3 个子线程 for (int i = 0; i < 3; i++) { new Thread(() -> { // 执行任务... latch.countDown(); // 完成一个,计数减 1 }).start(); } latch.await(); // 主线程阻塞,直到计数为 0 System.out.println("所有任务完成");
4. CyclicBarrier(多线程互相等待,一起进行下一轮)
// 允许 3 个线程互相等待,全部到达后一起执行 barrierAction CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("全员到达,继续下一轮")); for (int i = 0; i < 3; i++) { new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " 到达"); barrier.await(); // 等待其他线程 System.out.println(Thread.currentThread().getName() + " 继续执行"); } catch (Exception ignored) {} }).start(); }

五、 高级避坑指南(重要)

  1. 中断响应:使用lockInterruptibly()而非lock(),以便在等待锁时响应中断。

  2. 避免await()在循环外使用Conditionawait()必须放在while (条件不满足)循环中,防止虚假唤醒。

  3. CountDownLatch 不可重用:计数减到 0 后无法重置。如需重用,请用CyclicBarrier

  4. 读写锁的锁降级:写锁可以降级为读锁(先获取写锁,再获取读锁,再释放写锁),但读锁不能升级为写锁。

  5. 性能考量:非公平锁吞吐量通常更高,除非强需求避免使用公平锁。

六、 学习路径建议

  1. 初级:熟练使用ReentrantLock+Condition实现等待/通知,掌握Semaphore做限流。

  2. 中级:精读 AQS 源码中的acquireQueued()shouldParkAfterFailedAcquire(),理解节点状态流转(SIGNAL、CANCELLED)。

  3. 高级:尝试基于 AQS 自定义一个互斥锁(只需实现tryAcquiretryRelease),这能让你彻底理解模板方法模式。

http://www.jsqmd.com/news/1140429/

相关文章:

  • UnityExplorer自由视角相机:3步解锁游戏世界的终极探索方案
  • GPT-5.6 正式发布:三大模型、价格腰斩、150万上下文——OpenAI 的全面反击
  • ForesightFlow:势引导流匹配实现具身智能动作策略优化
  • 六自由度飞行模拟平台:航空模拟训练与研发的核心技术方案
  • AzurLaneAutoScript技术方案:解决碧蓝航线重复操作问题的自动化解决方案
  • Universal Extractor 2:解决复杂文件格式提取难题的终极方案
  • AI 辅助样式重构:把全局 CSS 转化为 CSS Modules
  • 如何3分钟解锁B站缓存视频?m4s-converter终极指南让你实现跨平台自由播放
  • 英雄联盟Akari助手:终极免费开源游戏效率提升工具完整使用指南
  • 如何快速掌握游戏修改:UE4SS脚本系统完整指南
  • 终极解决方案:一键修复Windows软件兼容性问题,告别“缺少DLL“错误
  • 3分钟搞定Visual C++运行库:一劳永逸的安装与修复指南
  • GBase 8a数据库gcrcman备份恢复工具介绍
  • 终极Windows 11界面定制指南:三步掌握ExplorerPatcher的强大功能
  • Visual C++运行库终极修复指南:一键解决DLL缺失问题的完整方案
  • JDK Concurrent Collections 设计及工作原理与教程
  • AI视觉项目实战指南:从PyTorch环境搭建到模型部署全流程
  • codebase-memory-mcp是什么?AI代码库记忆工具部署与使用指南
  • 冬青先令规格选择,设计尺寸要看哪些细节
  • 想要学习deepseekGEO技术?深圳这些优质靠谱课程值得打算入行的你了解
  • m4s-converter解决方案:B站缓存视频格式转换与个人备份工具
  • Hermes Agent部署指南:从环境准备到生产环境配置
  • CK2DLL双字节补丁:三步实现十字军之王II完美中文显示
  • ComfyUI单图生成3D模型:腾讯混元3D工作流配置与实践
  • IBM Condor 1121量子比特芯片解析:超导电路如何实现千比特级集成
  • B站m4s视频转换完全指南:3分钟解锁跨平台播放自由
  • 【ROS2】 Ubuntu 避坑指南:PyCharm 桌面快捷方式与 Conda 环境配置全记录
  • 懒人程序员的信息推送革命
  • 如何快速部署Skyline APIServer?Ubuntu与RHEL系统的终极安装教程
  • 【日记】看了那个小女孩的表演(2239字)