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

独占锁和共享锁唤醒机制

目录
  • 独占锁和共享锁唤醒机制
    • 精确总结
      • 独占锁(EXCLUSIVE)
      • 共享锁(SHARED)
    • 独占锁的正确顺序:
    • 共享锁的正确顺序:
    • 核心区别可视化
      • 独占锁(接力赛)
      • 共享锁(广播链)
    • 代码验证
    • 关键区别表格
    • 总结

独占锁和共享锁唤醒机制

精确总结

独占锁(EXCLUSIVE)

// 释放时:unlock() → release() → unparkSuccessor()
// 只唤醒队列中的第一个有效后继节点
// 唤醒后整个释放过程就结束了

特点

  • 一次释放,只唤醒一个线程
  • 被唤醒的线程获取锁后不会主动唤醒其他线程
  • 唤醒链不传播

共享锁(SHARED)

// 释放时:releaseShared() → doReleaseShared()
// 1. 先唤醒第一个后继节点
// 2. 被唤醒的线程在返回前,会调用setHeadAndPropagate()
// 3. setHeadAndPropagate()中会再次调用doReleaseShared()唤醒下一个
// 4. 形成唤醒链,直到没有需要唤醒的节点

特点

  • 一次释放,触发连锁唤醒
  • 被唤醒的线程会"接力"唤醒下一个
  • 唤醒链会一直传播

独占锁的正确顺序:

初始:线程1、2、3都在等待队列中head → 线程1 → 线程2 → 线程3 → tail1. 释放锁 → unparkSuccessor(head) → 唤醒线程1
2. 线程1被唤醒 → 获取锁 → 执行任务
3. 线程1执行完毕 → unlock() → 再次unparkSuccessor() → 唤醒线程2
4. 线程2被唤醒 → 获取锁 → 执行任务
5. 线程2执行完毕 → unlock() → 唤醒线程3
6. 线程3被唤醒 → 获取锁 → 执行任务关键:执行和唤醒是串行的,一个线程执行完才唤醒下一个

共享锁的正确顺序:

初始:线程1、2、3都在等待队列中  head → 线程1 → 线程2 → 线程3 → tail1. 释放资源 → doReleaseShared() → unparkSuccessor(head) → 唤醒线程1
2. 线程1被唤醒 → 获取共享资源 → 立即调用setHeadAndPropagate()
3. setHeadAndPropagate()中调用doReleaseShared() → 唤醒线程2
4. 线程2被唤醒 → 获取共享资源 → 立即调用setHeadAndPropagate()  
5. setHeadAndPropagate()中调用doReleaseShared() → 唤醒线程3
6. 线程3被唤醒 → 获取共享资源 → ...
7. 所有线程几乎同时开始执行(如果资源足够)关键:唤醒是链式、立即的,可能所有线程都被唤醒后才开始执行任务

核心区别可视化

独占:唤醒1 → [线程1执行] → 唤醒2 → [线程2执行] → 唤醒3 → [线程3执行]↑                     ↑                     ↑执行和唤醒严格交替      执行完才唤醒下一个       串行执行共享:唤醒1 → 唤醒2 → 唤醒3 → [线程1、2、3几乎同时开始执行]↑        ↑        ↑快速链式唤醒      不需要等待前一个执行完      并行/并发执行

独占锁(接力赛)

// 像接力棒,必须一个接一个
┌─────────┐    ┌─────────┐    ┌─────────┐
│ 唤醒线程1 │──→│线程1执行│──→│ 唤醒线程2 │──→│线程2执行│──→│ 唤醒线程3 │
└─────────┘    └─────────┘    └─────────┘    └─────────┘    └─────────┘时间:t1         t2            t3            t4            t5

共享锁(广播链)

// 像多米诺骨牌,快速连续触发
┌─────────┐    ┌─────────┐    ┌─────────┐
│ 唤醒线程1 │──→│ 唤醒线程2 │──→│ 唤醒线程3 │
└─────────┘    └─────────┘    └─────────┘│             │             │↓             ↓             ↓
┌─────────┐    ┌─────────┐    ┌─────────┐
│线程1执行│    │线程2执行│    │线程3执行│    ← 几乎同时开始
└─────────┘    └─────────┘    └─────────┘时间:t2         t2           t2      (时间很接近)

代码验证

public class WakeupTimingDemo {static long startTime;public static void main(String[] args) throws InterruptedException {System.out.println("=== 测试独占锁唤醒时序 ===");testExclusiveTiming();Thread.sleep(1000);System.out.println("\n=== 测试共享锁唤醒时序 ===");testSharedTiming();}static void testExclusiveTiming() throws InterruptedException {ReentrantLock lock = new ReentrantLock();startTime = System.currentTimeMillis();// 主线程先获取锁lock.lock();// 创建3个线程for (int i = 1; i <= 3; i++) {final int id = i;new Thread(() -> {lock.lock();  // 等待锁try {long time = System.currentTimeMillis() - startTime;System.out.printf("  线程%d 在 %dms 后获得锁,执行1秒\n", id, time);Thread.sleep(1000);  // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}).start();}Thread.sleep(100);System.out.println("主线程在 100ms 释放锁");lock.unlock();  // 释放锁,开始唤醒链Thread.sleep(5000);}static void testSharedTiming() throws InterruptedException {Semaphore semaphore = new Semaphore(0);  // 0个许可,所有线程都会阻塞startTime = System.currentTimeMillis();// 创建3个线程for (int i = 1; i <= 3; i++) {final int id = i;new Thread(() -> {try {semaphore.acquire();  // 等待许可long time = System.currentTimeMillis() - startTime;System.out.printf("  线程%d 在 %dms 后被唤醒,执行1秒\n", id, time);Thread.sleep(1000);  // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}}).start();}Thread.sleep(100);System.out.println("主线程在 100ms 释放1个许可");semaphore.release(3);  // 释放3个许可,会链式唤醒Thread.sleep(5000);}
}

可能的输出

=== 测试独占锁唤醒时序 ===
主线程在 100ms 释放锁线程1 在 100ms 后获得锁,执行1秒线程2 在 1100ms 后获得锁,执行1秒  ← 注意:线程1执行完才唤醒线程2线程3 在 2100ms 后获得锁,执行1秒  ← 线程2执行完才唤醒线程3=== 测试共享锁唤醒时序 ===  
主线程在 100ms 释放1个许可线程1 在 100ms 后被唤醒,执行1秒线程2 在 101ms 后被唤醒,执行1秒  ← 几乎同时被唤醒!线程3 在 102ms 后被唤醒,执行1秒  ← 几乎同时被唤醒!

关键区别表格

对比项 独占锁 共享锁
唤醒时机 前一个线程执行完后才唤醒下一个 立即链式唤醒,不管前一个是否开始执行
执行时机 严格串行,一个接一个 可能并发执行,特别是资源足够时
唤醒链 执行完才传递"唤醒权" 唤醒时就传递"唤醒权"
性能影响 延迟大,吞吐量低 延迟小,吞吐量高

总结

时间顺序:

  1. 独占锁是"执行-唤醒-执行-唤醒"的串行模式
  2. 共享锁是"唤醒-唤醒-唤醒-执行"的快速传播模式

这也是为什么共享锁(如Semaphore)适合高并发场景的原因:它能快速唤醒大量等待线程,提高系统吞吐量。而独占锁(如ReentrantLock)则更注重互斥性和公平性。

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

相关文章:

  • 个人学习---25.12.9
  • 大模型完整架构
  • iOS 知识点 - 一篇文章带你串通「操作系统 内存模型 文件系统」
  • 2025年12月天津金蝶软件代理商最新推荐:天津鹏越软件,金蝶云星空、金蝶云星晨、金蝶云星翰、助力企业高效落地ERP系统与全场景管理升级
  • 102302114_比山布努尔兰_作业4
  • 2025年12月济南艺考画室最新推荐:济南大道画室,济南艺考画室、济南画室哪家好、济南画室推荐、山东美术艺考培训、山东画室个性化教学新标杆
  • 记一次磁盘占满的问题
  • Redis提供的原子性命令
  • 多业态连锁环境管理系统:AI + 机器人闭环,坪效提升 16%
  • 2025年12月室内水上乐园厂家推荐:山东汇川,儿童水上乐园、大型水上乐园、主题水上乐园、室外水上乐园、恒温泳池、室 泳池、全场景适配新标杆
  • 2025雅思培训班怎么选?这5家高性价比机构帮你高效提分
  • 2025年12月水上乐园设备厂家最新推荐:昊至泉充气水上乐园设备、室内水上乐园设备、户外水上乐园设备、大型水上乐园设备、漂流河水上乐园设备、打造安全创新个性化水上娱乐新标准
  • 实用指南:「腾讯云NoSQL」技术之向量数据库篇:自研分布式向量数据库,实现毫秒级时序一致备份的挑战和实践
  • py-lambda-map-list随笔
  • 杂题选记
  • 2025年12月铝材厂家推荐榜:廊坊国美铝业,工业铝材、门窗铝材、3C铝材、通用铝材、多领域铝材定制与绿色生产标杆
  • Qt 文本转语言(QTextToSpeech类)详解 - 实践
  • AWS发布网络扫描指南:构建更安全云环境的守则
  • # 题解#洛谷P2880 Balanced Lineup #ST表#
  • 2025年12月包头保洁公司最新推荐:信达家政,包头保洁开荒、包头高空清洗保洁、包头保姆公司、包头保姆家政、包头保姆月嫂、包头保姆护工、服务品质新标准
  • 机器视觉测量与建模
  • 2025最值得报的雅思封闭班:高性价比/冲高分/打基础三类优选清单
  • 为什么会诞生流形的概念?
  • [Java EE] 多线程 -- 初阶(1) - 详解
  • 2025年12月丝杆升降机标杆厂家最新推荐:德州德特机械,螺旋升降机、sjb螺旋升降机、zimm螺旋升降机、SJA螺旋升降机、联动丝杆升降机、螺旋丝杆升降机、专注精密传动新标准
  • AQS与CAS深度讲解
  • 2025年唐老狮权威解读:游戏开发课的体系化构建优势
  • 2025年12月注浆工程厂家推荐:安徽林固,道路注浆、空鼓注浆、公路注浆、路基注浆、地基注浆、厂房注浆、地坪注浆、矿山注浆、多场景注浆解决方案服务商
  • PROFILE
  • PKU 数据结构与算法 2025 复习题 坐公交