商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Java 中 Semaphore 内部如何利用 AQS 实现同步等待

Java 中 Semaphore 内部如何利用 AQS 实现同步等待

  发布于2026-07-12 阅读(0)

扫一扫,手机访问

说到 Semaphore,它的底层实现其实很简单——就是基于 AQS(AbstractQueuedSynchronizer)那一套队列管理和状态控制机制。不过,它不像 ReentrantLock 那样跟线程绑定,而是纯粹靠一个整数状态(state)来代表当前还剩多少许可。所有同步逻辑,获取、释放、阻塞、唤醒,全都围绕这个整型数字展开。

Java 中 Semaphore 内部如何利用 AQS 实现同步等待

具体来说,AQS 的 state 字段在 Semaphore 里直接被当作可用许可计数器来用。构造时通过 setState(permits) 设好初始值,那就是总许可数。调用 acquire() 时,state -= 1,如果减完后变负数,说明许可用光了,当前线程就得乖乖入队等着。而 release() 则是 state += 1,释放后立马尝试唤醒队列头部的等待线程。整个过程不记录哪个线程持有许可——换句话说,任何线程都可以释放,不像锁那样有“持有者”的概念。

state 直接代表 permits 数量

  • 构造时调用 setState(permits),初始值即为总许可数
  • acquire() 对应 state -= 1;若减后为负,说明无许可可用,当前线程入队等待
  • release() 对应 state += 1;释放后立即尝试唤醒队列头部等待线程
  • 整个过程不记录持有线程(区别于 ReentrantLock),任意线程都可 release

共享式获取逻辑走 tryAcquireShared

Semaphore 用的是 AQS 的共享模式(shared mode),核心就是重写 tryAcquireShared(int) 方法。这个方法返回值决定是否获取成功:≥0 表示成功,负数表示失败。内部通过 CAS 原子更新 state,避免竞态条件;如果 CAS 失败,就会进入 AQS 的 doAcquireSharedInterruptibly 流程,把等待线程封装成 Node 节点,塞进 CLH 同步队列,挂起前还会检查前驱节点的状态,保证唤醒链足够可靠。

公平性由 Sync 子类控制

Semaphore 提供了公平和非公平两种策略,区别就体现在 tryAcquireShared 的具体实现里。

  • 非公平模式(默认):直接尝试 CAS 获取许可,不查队列,完全可能“插队”
  • 公平模式:先调用 hasQueuedPredecessors(),如果队列里有线程在排队,就放弃抢锁,老老实实排到队尾
  • 两者共用同一套 state 管理和队列唤醒逻辑,唯一差别就是“能否抢先尝试”

释放操作触发级联唤醒

release() 最终会调用 tryReleaseShared():先原子地将 state 加 1,然后返回 true;之后 AQS 自动调用 doReleaseShared(),从队列头开始唤醒一个或多个等待线程。因为是共享模式,被唤醒的线程会再次调用 tryAcquireShared 去竞争许可,这样就形成了“释放→唤醒→再竞争”的循环,一个释放操作可以带动多个线程依次获得许可,效率很高。

本文转载于:https://www.php.cn/faq/2812463.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注