AQS 与 ReentrantLock:队列同步器与可重入锁

如果说 synchronized 是 JVM 给你的内置锁,那 AQS 就是 JUC 里很多同步工具的地基。

ReentrantLockSemaphoreCountDownLatch 这些类看起来用法不同,但底层都有一套相似的骨架:一个 state 表示资源状态,一条 FIFO 队列保存等待线程,再用 CAS 保证抢资源时的原子性。

这套骨架就是 AQS。

AQS 是什么

AQS,全称 AbstractQueuedSynchronizer,抽象队列同步器。

它不是一个直接拿来用的业务工具,而是用来构建锁和同步组件的基础框架。

常见基于 AQS 的组件:

组件 用途
ReentrantLock 可重入互斥锁
Semaphore 信号量,控制并发访问数量
CountDownLatch 倒计时锁,等待多个任务完成
ReentrantReadWriteLock 读写锁

AQS 的核心结构

AQS 最核心的东西有两个:

  1. volatile int state
  2. 一个 FIFO 双向队列。

state 表示同步状态。不同工具对它的解释不同。

ReentrantLock 里,state = 0 通常表示没有线程持锁,state > 0 表示锁被持有,并且数值可以表示重入次数。

Semaphore 里,state 可以表示剩余许可数。

CountDownLatch 里,state 可以表示还没倒完的计数。
#mermaid-svg-20U0KfDEdYM7PMHP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-20U0KfDEdYM7PMHP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-20U0KfDEdYM7PMHP .error-icon{fill:#552222;}#mermaid-svg-20U0KfDEdYM7PMHP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-20U0KfDEdYM7PMHP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-20U0KfDEdYM7PMHP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-20U0KfDEdYM7PMHP .marker.cross{stroke:#333333;}#mermaid-svg-20U0KfDEdYM7PMHP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-20U0KfDEdYM7PMHP p{margin:0;}#mermaid-svg-20U0KfDEdYM7PMHP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-20U0KfDEdYM7PMHP .cluster-label text{fill:#333;}#mermaid-svg-20U0KfDEdYM7PMHP .cluster-label span{color:#333;}#mermaid-svg-20U0KfDEdYM7PMHP .cluster-label span p{background-color:transparent;}#mermaid-svg-20U0KfDEdYM7PMHP .label text,#mermaid-svg-20U0KfDEdYM7PMHP span{fill:#333;color:#333;}#mermaid-svg-20U0KfDEdYM7PMHP .node rect,#mermaid-svg-20U0KfDEdYM7PMHP .node circle,#mermaid-svg-20U0KfDEdYM7PMHP .node ellipse,#mermaid-svg-20U0KfDEdYM7PMHP .node polygon,#mermaid-svg-20U0KfDEdYM7PMHP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-20U0KfDEdYM7PMHP .rough-node .label text,#mermaid-svg-20U0KfDEdYM7PMHP .node .label text,#mermaid-svg-20U0KfDEdYM7PMHP .image-shape .label,#mermaid-svg-20U0KfDEdYM7PMHP .icon-shape .label{text-anchor:middle;}#mermaid-svg-20U0KfDEdYM7PMHP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-20U0KfDEdYM7PMHP .rough-node .label,#mermaid-svg-20U0KfDEdYM7PMHP .node .label,#mermaid-svg-20U0KfDEdYM7PMHP .image-shape .label,#mermaid-svg-20U0KfDEdYM7PMHP .icon-shape .label{text-align:center;}#mermaid-svg-20U0KfDEdYM7PMHP .node.clickable{cursor:pointer;}#mermaid-svg-20U0KfDEdYM7PMHP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-20U0KfDEdYM7PMHP .arrowheadPath{fill:#333333;}#mermaid-svg-20U0KfDEdYM7PMHP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-20U0KfDEdYM7PMHP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-20U0KfDEdYM7PMHP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-20U0KfDEdYM7PMHP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-20U0KfDEdYM7PMHP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-20U0KfDEdYM7PMHP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-20U0KfDEdYM7PMHP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-20U0KfDEdYM7PMHP .cluster text{fill:#333;}#mermaid-svg-20U0KfDEdYM7PMHP .cluster span{color:#333;}#mermaid-svg-20U0KfDEdYM7PMHP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-20U0KfDEdYM7PMHP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-20U0KfDEdYM7PMHP rect.text{fill:none;stroke-width:0;}#mermaid-svg-20U0KfDEdYM7PMHP .icon-shape,#mermaid-svg-20U0KfDEdYM7PMHP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-20U0KfDEdYM7PMHP .icon-shape p,#mermaid-svg-20U0KfDEdYM7PMHP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-20U0KfDEdYM7PMHP .icon-shape .label rect,#mermaid-svg-20U0KfDEdYM7PMHP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-20U0KfDEdYM7PMHP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-20U0KfDEdYM7PMHP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-20U0KfDEdYM7PMHP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AQS
volatile int state
FIFO 双向等待队列
head
Node: Thread-1
Node: Thread-2
tail
CAS 修改资源状态

这就是 AQS 的漂亮之处:它把"抢资源、失败排队、唤醒后继"这些通用流程抽出来,让不同同步工具只需要定义怎么获取和释放资源。

抢锁过程怎么走

以互斥锁为例,线程来抢资源时大概这么走:
#mermaid-svg-ZOI7TFc1pgXJEk83{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZOI7TFc1pgXJEk83 .error-icon{fill:#552222;}#mermaid-svg-ZOI7TFc1pgXJEk83 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZOI7TFc1pgXJEk83 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .marker.cross{stroke:#333333;}#mermaid-svg-ZOI7TFc1pgXJEk83 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZOI7TFc1pgXJEk83 p{margin:0;}#mermaid-svg-ZOI7TFc1pgXJEk83 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .cluster-label text{fill:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .cluster-label span{color:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .cluster-label span p{background-color:transparent;}#mermaid-svg-ZOI7TFc1pgXJEk83 .label text,#mermaid-svg-ZOI7TFc1pgXJEk83 span{fill:#333;color:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .node rect,#mermaid-svg-ZOI7TFc1pgXJEk83 .node circle,#mermaid-svg-ZOI7TFc1pgXJEk83 .node ellipse,#mermaid-svg-ZOI7TFc1pgXJEk83 .node polygon,#mermaid-svg-ZOI7TFc1pgXJEk83 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .rough-node .label text,#mermaid-svg-ZOI7TFc1pgXJEk83 .node .label text,#mermaid-svg-ZOI7TFc1pgXJEk83 .image-shape .label,#mermaid-svg-ZOI7TFc1pgXJEk83 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZOI7TFc1pgXJEk83 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .rough-node .label,#mermaid-svg-ZOI7TFc1pgXJEk83 .node .label,#mermaid-svg-ZOI7TFc1pgXJEk83 .image-shape .label,#mermaid-svg-ZOI7TFc1pgXJEk83 .icon-shape .label{text-align:center;}#mermaid-svg-ZOI7TFc1pgXJEk83 .node.clickable{cursor:pointer;}#mermaid-svg-ZOI7TFc1pgXJEk83 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .arrowheadPath{fill:#333333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZOI7TFc1pgXJEk83 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZOI7TFc1pgXJEk83 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZOI7TFc1pgXJEk83 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZOI7TFc1pgXJEk83 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .cluster text{fill:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 .cluster span{color:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZOI7TFc1pgXJEk83 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZOI7TFc1pgXJEk83 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZOI7TFc1pgXJEk83 .icon-shape,#mermaid-svg-ZOI7TFc1pgXJEk83 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZOI7TFc1pgXJEk83 .icon-shape p,#mermaid-svg-ZOI7TFc1pgXJEk83 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZOI7TFc1pgXJEk83 .icon-shape .label rect,#mermaid-svg-ZOI7TFc1pgXJEk83 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZOI7TFc1pgXJEk83 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZOI7TFc1pgXJEk83 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZOI7TFc1pgXJEk83 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



线程尝试获取资源
state 是否可获取
CAS 修改 state
CAS 是否成功
获取成功
封装成 Node 入队
在 AQS 队列中等待
前驱释放资源后被唤醒

这里的关键点是 CAS。

多个线程同时看到 state = 0 时,只有一个线程能 CAS 成功。其他线程 CAS 失败后,就会进入队列等待。

AQS 公平还是非公平

AQS 本身既能支持公平,也能支持非公平。具体公平不公平,看上层同步器怎么实现。

公平锁的思路是:新来的线程先看队列里有没有人排队。如果有人排队,就不要插队,去队尾等。

非公平锁的思路是:新来的线程可以先尝试抢一下。抢到了就直接执行,抢不到再排队。
#mermaid-svg-ouiG7txCh0rWUED4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ouiG7txCh0rWUED4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ouiG7txCh0rWUED4 .error-icon{fill:#552222;}#mermaid-svg-ouiG7txCh0rWUED4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ouiG7txCh0rWUED4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ouiG7txCh0rWUED4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ouiG7txCh0rWUED4 .marker.cross{stroke:#333333;}#mermaid-svg-ouiG7txCh0rWUED4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ouiG7txCh0rWUED4 p{margin:0;}#mermaid-svg-ouiG7txCh0rWUED4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ouiG7txCh0rWUED4 .cluster-label text{fill:#333;}#mermaid-svg-ouiG7txCh0rWUED4 .cluster-label span{color:#333;}#mermaid-svg-ouiG7txCh0rWUED4 .cluster-label span p{background-color:transparent;}#mermaid-svg-ouiG7txCh0rWUED4 .label text,#mermaid-svg-ouiG7txCh0rWUED4 span{fill:#333;color:#333;}#mermaid-svg-ouiG7txCh0rWUED4 .node rect,#mermaid-svg-ouiG7txCh0rWUED4 .node circle,#mermaid-svg-ouiG7txCh0rWUED4 .node ellipse,#mermaid-svg-ouiG7txCh0rWUED4 .node polygon,#mermaid-svg-ouiG7txCh0rWUED4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ouiG7txCh0rWUED4 .rough-node .label text,#mermaid-svg-ouiG7txCh0rWUED4 .node .label text,#mermaid-svg-ouiG7txCh0rWUED4 .image-shape .label,#mermaid-svg-ouiG7txCh0rWUED4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ouiG7txCh0rWUED4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ouiG7txCh0rWUED4 .rough-node .label,#mermaid-svg-ouiG7txCh0rWUED4 .node .label,#mermaid-svg-ouiG7txCh0rWUED4 .image-shape .label,#mermaid-svg-ouiG7txCh0rWUED4 .icon-shape .label{text-align:center;}#mermaid-svg-ouiG7txCh0rWUED4 .node.clickable{cursor:pointer;}#mermaid-svg-ouiG7txCh0rWUED4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ouiG7txCh0rWUED4 .arrowheadPath{fill:#333333;}#mermaid-svg-ouiG7txCh0rWUED4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ouiG7txCh0rWUED4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ouiG7txCh0rWUED4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ouiG7txCh0rWUED4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ouiG7txCh0rWUED4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ouiG7txCh0rWUED4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ouiG7txCh0rWUED4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ouiG7txCh0rWUED4 .cluster text{fill:#333;}#mermaid-svg-ouiG7txCh0rWUED4 .cluster span{color:#333;}#mermaid-svg-ouiG7txCh0rWUED4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ouiG7txCh0rWUED4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ouiG7txCh0rWUED4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ouiG7txCh0rWUED4 .icon-shape,#mermaid-svg-ouiG7txCh0rWUED4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ouiG7txCh0rWUED4 .icon-shape p,#mermaid-svg-ouiG7txCh0rWUED4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ouiG7txCh0rWUED4 .icon-shape .label rect,#mermaid-svg-ouiG7txCh0rWUED4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ouiG7txCh0rWUED4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ouiG7txCh0rWUED4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ouiG7txCh0rWUED4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

没有



新线程到来
公平锁?
队列里是否有等待线程
进入队列排队
尝试 CAS 抢锁
抢锁成功?
执行临界区

非公平锁吞吐量通常更高,因为减少了严格排队带来的调度成本。但它可能让队列里的老线程等得更久。

ReentrantLock 有哪些能力

ReentrantLock 是 JUC 里的可重入锁。它和 synchronized 一样能互斥、能重入,但提供了更多控制能力:

能力 synchronized ReentrantLock
自动释放锁 否,需要 unlock()
可重入
公平锁 不支持显式选择 构造器可选择
可中断等待 不方便 lockInterruptibly()
超时获取锁 不支持 tryLock(timeout, unit)
多条件队列 一个 WaitSet 多个 Condition

基本用法一定要写成 try finally

java 复制代码
ReentrantLock lock = new ReentrantLock();

lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();
}

ReentrantLock 不会像 synchronized 那样自动释放锁。如果忘了 unlock(),后面的线程可能永远等下去。

公平锁和非公平锁

ReentrantLock 默认是非公平锁:

java 复制代码
ReentrantLock lock = new ReentrantLock();

也可以传 true 创建公平锁:

java 复制代码
ReentrantLock fairLock = new ReentrantLock(true);

内部大致是选择不同的 Sync 实现:

java 复制代码
public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

非公平锁不是"乱来",它只是允许新线程先尝试 CAS 抢锁。如果失败,还是会进入 AQS 队列。

可中断和可超时

synchronized 的一个限制是,线程如果阻塞在获取锁上,不太方便被取消。

ReentrantLock 可以用 lockInterruptibly()

java 复制代码
try {
    lock.lockInterruptibly();
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    return;
}

try {
    // 临界区
} finally {
    lock.unlock();
}

也可以用 tryLock() 设置超时时间:

java 复制代码
if (!lock.tryLock(2, TimeUnit.SECONDS)) {
    System.out.println("获取锁失败");
    return;
}

try {
    // 临界区
} finally {
    lock.unlock();
}

这在业务系统里很有用。比如一个操作拿不到锁就快速失败,不要一直把请求线程挂死。

多条件变量

synchronized 每个对象只有一个 WaitSet,调用 notify()notifyAll() 时很难精准唤醒某一类等待线程。

ReentrantLock 可以创建多个 Condition

java 复制代码
static ReentrantLock lock = new ReentrantLock();
static Condition c1 = lock.newCondition();
static Condition c2 = lock.newCondition();

不同条件的线程进入不同等待队列,需要时可以精准唤醒。
#mermaid-svg-4M2x8FoGEG0vMDEh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4M2x8FoGEG0vMDEh .error-icon{fill:#552222;}#mermaid-svg-4M2x8FoGEG0vMDEh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4M2x8FoGEG0vMDEh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4M2x8FoGEG0vMDEh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4M2x8FoGEG0vMDEh .marker.cross{stroke:#333333;}#mermaid-svg-4M2x8FoGEG0vMDEh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4M2x8FoGEG0vMDEh p{margin:0;}#mermaid-svg-4M2x8FoGEG0vMDEh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh .cluster-label text{fill:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh .cluster-label span{color:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh .cluster-label span p{background-color:transparent;}#mermaid-svg-4M2x8FoGEG0vMDEh .label text,#mermaid-svg-4M2x8FoGEG0vMDEh span{fill:#333;color:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh .node rect,#mermaid-svg-4M2x8FoGEG0vMDEh .node circle,#mermaid-svg-4M2x8FoGEG0vMDEh .node ellipse,#mermaid-svg-4M2x8FoGEG0vMDEh .node polygon,#mermaid-svg-4M2x8FoGEG0vMDEh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4M2x8FoGEG0vMDEh .rough-node .label text,#mermaid-svg-4M2x8FoGEG0vMDEh .node .label text,#mermaid-svg-4M2x8FoGEG0vMDEh .image-shape .label,#mermaid-svg-4M2x8FoGEG0vMDEh .icon-shape .label{text-anchor:middle;}#mermaid-svg-4M2x8FoGEG0vMDEh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4M2x8FoGEG0vMDEh .rough-node .label,#mermaid-svg-4M2x8FoGEG0vMDEh .node .label,#mermaid-svg-4M2x8FoGEG0vMDEh .image-shape .label,#mermaid-svg-4M2x8FoGEG0vMDEh .icon-shape .label{text-align:center;}#mermaid-svg-4M2x8FoGEG0vMDEh .node.clickable{cursor:pointer;}#mermaid-svg-4M2x8FoGEG0vMDEh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4M2x8FoGEG0vMDEh .arrowheadPath{fill:#333333;}#mermaid-svg-4M2x8FoGEG0vMDEh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4M2x8FoGEG0vMDEh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4M2x8FoGEG0vMDEh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4M2x8FoGEG0vMDEh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4M2x8FoGEG0vMDEh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4M2x8FoGEG0vMDEh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4M2x8FoGEG0vMDEh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4M2x8FoGEG0vMDEh .cluster text{fill:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh .cluster span{color:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4M2x8FoGEG0vMDEh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4M2x8FoGEG0vMDEh rect.text{fill:none;stroke-width:0;}#mermaid-svg-4M2x8FoGEG0vMDEh .icon-shape,#mermaid-svg-4M2x8FoGEG0vMDEh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4M2x8FoGEG0vMDEh .icon-shape p,#mermaid-svg-4M2x8FoGEG0vMDEh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4M2x8FoGEG0vMDEh .icon-shape .label rect,#mermaid-svg-4M2x8FoGEG0vMDEh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4M2x8FoGEG0vMDEh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4M2x8FoGEG0vMDEh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4M2x8FoGEG0vMDEh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ReentrantLock
AQS 同步队列
Condition c1 等待队列
Condition c2 等待队列
c1.signal / signalAll
c2.signal / signalAll

注意,await()signal() 这些方法也必须在持有锁的情况下调用。

synchronized 和 Lock 怎么比较

可以从三个层面说。

语法层面:

synchronized 是关键字,JVM 层面实现,退出同步代码块自动释放锁。Lock 是接口,JDK 层面实现,需要手动释放锁。

功能层面:

二者都能互斥、同步、重入。Lock 额外支持公平锁、可中断、可超时、多条件变量等能力。

性能层面:

早期 synchronized 性能较弱,但后来有偏向锁、轻量级锁等优化,低竞争场景并不差。竞争复杂、需要更多控制能力时,ReentrantLock 更灵活。

面试怎么答

可以这样讲:

AQS 是 JUC 里构建锁和同步工具的基础框架,核心是一个 volatile int state 和一条 FIFO 双向队列。线程获取资源时会先用 CAS 修改 state,成功就获取资源,失败就封装成 Node 进入队列等待。

ReentrantLock 底层就是基于 AQS 实现的。它默认是非公平锁,也可以通过构造器创建公平锁。非公平锁允许新来的线程先 CAS 抢一下锁,公平锁会先判断队列里是否已有等待线程。

相比 synchronizedReentrantLock 支持可中断、可超时、公平锁、多条件变量,但必须在 finally 里手动 unlock()。如果只是简单互斥,synchronized 更简洁;如果需要更强控制能力,用 ReentrantLock 更合适。

相关推荐
码语智行2 小时前
系统启动时初始化数据功能分析
java·spring boot
得物技术2 小时前
用 LLM Agent 重构告警排查流程|得物技术
java·人工智能·后端
Byron__2 小时前
RabbitMQ 面试核心精讲
java·面试·rabbitmq
Java面试题总结2 小时前
AgentScope Harness 深度实战:让Java智能体从“Demo可用”走向“生产可用”
java·开发语言·wpf
JAVA面经实录9172 小时前
MQ高频面试题标准答案(Java后端/架构面试背诵版)
java·面试·架构·kafka·rabbitmq
码不停蹄的玄黓3 小时前
AQS底层原理
java
糖果店的幽灵3 小时前
Claude Code 完全实战指南 - 第四章:Skill 怎么写
java·服务器·前端
jeffer_liu3 小时前
Spring AI 生产级实战:记忆管理
java·人工智能·后端·spring·语言模型
憧憬成为java架构高手的小白3 小时前
git工作流程简化版
java·spring boot·git