synchronized 底层原理:Monitor、对象头、Mark Word 与锁升级

synchronized 是 Java 并发里绕不开的一道坎。

它表面上很简单,给方法或代码块加个关键字,就能保证同一时刻只有一个线程进入临界区。但面试官真正想听的不是"它能加锁",而是你能不能讲清楚:这把锁到底锁在哪里,线程没抢到锁去了哪里,为什么还会有偏向锁、轻量级锁、重量级锁这些说法。

synchronized 先解决什么问题

看一个抢票例子:

java 复制代码
public class TicketDemo {
    static final Object lock = new Object();
    int ticketNum = 10;

    public void getTicket() {
        synchronized (lock) {
            if (ticketNum <= 0) {
                return;
            }
            System.out.println(Thread.currentThread().getName()
                    + " 抢到一张票, 剩余:" + ticketNum);
            ticketNum--;
        }
    }
}

ticketNum-- 不是一个原子操作。它至少包含读取、计算、写回几个动作。多个线程同时进来时,就可能出现重复扣减、超卖这类问题。

synchronized 的核心作用就是互斥:同一时刻最多只有一个线程持有某个对象锁,进入对应的同步代码块。
#mermaid-svg-DqklKVSWpspxommv{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-DqklKVSWpspxommv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DqklKVSWpspxommv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DqklKVSWpspxommv .error-icon{fill:#552222;}#mermaid-svg-DqklKVSWpspxommv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DqklKVSWpspxommv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DqklKVSWpspxommv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DqklKVSWpspxommv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DqklKVSWpspxommv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DqklKVSWpspxommv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DqklKVSWpspxommv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DqklKVSWpspxommv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DqklKVSWpspxommv .marker.cross{stroke:#333333;}#mermaid-svg-DqklKVSWpspxommv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DqklKVSWpspxommv p{margin:0;}#mermaid-svg-DqklKVSWpspxommv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DqklKVSWpspxommv .cluster-label text{fill:#333;}#mermaid-svg-DqklKVSWpspxommv .cluster-label span{color:#333;}#mermaid-svg-DqklKVSWpspxommv .cluster-label span p{background-color:transparent;}#mermaid-svg-DqklKVSWpspxommv .label text,#mermaid-svg-DqklKVSWpspxommv span{fill:#333;color:#333;}#mermaid-svg-DqklKVSWpspxommv .node rect,#mermaid-svg-DqklKVSWpspxommv .node circle,#mermaid-svg-DqklKVSWpspxommv .node ellipse,#mermaid-svg-DqklKVSWpspxommv .node polygon,#mermaid-svg-DqklKVSWpspxommv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DqklKVSWpspxommv .rough-node .label text,#mermaid-svg-DqklKVSWpspxommv .node .label text,#mermaid-svg-DqklKVSWpspxommv .image-shape .label,#mermaid-svg-DqklKVSWpspxommv .icon-shape .label{text-anchor:middle;}#mermaid-svg-DqklKVSWpspxommv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DqklKVSWpspxommv .rough-node .label,#mermaid-svg-DqklKVSWpspxommv .node .label,#mermaid-svg-DqklKVSWpspxommv .image-shape .label,#mermaid-svg-DqklKVSWpspxommv .icon-shape .label{text-align:center;}#mermaid-svg-DqklKVSWpspxommv .node.clickable{cursor:pointer;}#mermaid-svg-DqklKVSWpspxommv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DqklKVSWpspxommv .arrowheadPath{fill:#333333;}#mermaid-svg-DqklKVSWpspxommv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DqklKVSWpspxommv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DqklKVSWpspxommv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DqklKVSWpspxommv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DqklKVSWpspxommv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DqklKVSWpspxommv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DqklKVSWpspxommv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DqklKVSWpspxommv .cluster text{fill:#333;}#mermaid-svg-DqklKVSWpspxommv .cluster span{color:#333;}#mermaid-svg-DqklKVSWpspxommv 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-DqklKVSWpspxommv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DqklKVSWpspxommv rect.text{fill:none;stroke-width:0;}#mermaid-svg-DqklKVSWpspxommv .icon-shape,#mermaid-svg-DqklKVSWpspxommv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DqklKVSWpspxommv .icon-shape p,#mermaid-svg-DqklKVSWpspxommv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DqklKVSWpspxommv .icon-shape .label rect,#mermaid-svg-DqklKVSWpspxommv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DqklKVSWpspxommv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DqklKVSWpspxommv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DqklKVSWpspxommv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 能
不能
多个线程进入 getTicket
能否拿到 lock 对象锁
进入 synchronized 代码块
检查库存并扣减
退出代码块释放锁
阻塞等待
等待线程重新竞争锁

字节码里怎么体现

如果是同步代码块,编译后会看到 monitorentermonitorexit 指令。

java 复制代码
public class SyncTest {
    static final Object lock = new Object();
    static int counter = 0;

    public static void main(String[] args) {
        synchronized (lock) {
            counter++;
        }
    }
}

大致可以理解成:

text 复制代码
monitorenter
    counter++
monitorexit

如果代码块中抛了异常,也要释放锁,所以字节码里通常会有异常路径对应的 monitorexit

同步方法不一定直接出现这两个指令,它会在方法访问标志上加 ACC_SYNCHRONIZED。但底层思想一样,都是进入方法前获取 monitor,退出方法时释放 monitor。

Monitor 是什么

每个 Java 对象都可以关联一个 Monitor。Monitor 可以粗略理解成 JVM 里的监视器对象,重量级锁就是基于它实现的。

Monitor 里最重要的三个区域:

结构 作用
Owner 当前持有锁的线程,只能有一个
EntryList 没抢到锁、处于 BLOCKED 的线程
WaitSet 调用了 wait()、处于 WAITING 的线程

#mermaid-svg-OfUu4hlmJ9UjVIxY{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-OfUu4hlmJ9UjVIxY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OfUu4hlmJ9UjVIxY .error-icon{fill:#552222;}#mermaid-svg-OfUu4hlmJ9UjVIxY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OfUu4hlmJ9UjVIxY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .marker.cross{stroke:#333333;}#mermaid-svg-OfUu4hlmJ9UjVIxY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OfUu4hlmJ9UjVIxY p{margin:0;}#mermaid-svg-OfUu4hlmJ9UjVIxY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .cluster-label text{fill:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .cluster-label span{color:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .cluster-label span p{background-color:transparent;}#mermaid-svg-OfUu4hlmJ9UjVIxY .label text,#mermaid-svg-OfUu4hlmJ9UjVIxY span{fill:#333;color:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .node rect,#mermaid-svg-OfUu4hlmJ9UjVIxY .node circle,#mermaid-svg-OfUu4hlmJ9UjVIxY .node ellipse,#mermaid-svg-OfUu4hlmJ9UjVIxY .node polygon,#mermaid-svg-OfUu4hlmJ9UjVIxY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .rough-node .label text,#mermaid-svg-OfUu4hlmJ9UjVIxY .node .label text,#mermaid-svg-OfUu4hlmJ9UjVIxY .image-shape .label,#mermaid-svg-OfUu4hlmJ9UjVIxY .icon-shape .label{text-anchor:middle;}#mermaid-svg-OfUu4hlmJ9UjVIxY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .rough-node .label,#mermaid-svg-OfUu4hlmJ9UjVIxY .node .label,#mermaid-svg-OfUu4hlmJ9UjVIxY .image-shape .label,#mermaid-svg-OfUu4hlmJ9UjVIxY .icon-shape .label{text-align:center;}#mermaid-svg-OfUu4hlmJ9UjVIxY .node.clickable{cursor:pointer;}#mermaid-svg-OfUu4hlmJ9UjVIxY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .arrowheadPath{fill:#333333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OfUu4hlmJ9UjVIxY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OfUu4hlmJ9UjVIxY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OfUu4hlmJ9UjVIxY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OfUu4hlmJ9UjVIxY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .cluster text{fill:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY .cluster span{color:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY 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-OfUu4hlmJ9UjVIxY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OfUu4hlmJ9UjVIxY rect.text{fill:none;stroke-width:0;}#mermaid-svg-OfUu4hlmJ9UjVIxY .icon-shape,#mermaid-svg-OfUu4hlmJ9UjVIxY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OfUu4hlmJ9UjVIxY .icon-shape p,#mermaid-svg-OfUu4hlmJ9UjVIxY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OfUu4hlmJ9UjVIxY .icon-shape .label rect,#mermaid-svg-OfUu4hlmJ9UjVIxY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OfUu4hlmJ9UjVIxY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OfUu4hlmJ9UjVIxY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OfUu4hlmJ9UjVIxY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java 对象
关联 Monitor
Owner: 当前持锁线程
EntryList: 等锁线程
WaitSet: wait 等待线程
Thread-1
Thread-2 / Thread-3
Thread-4

这也解释了 BLOCKEDWAITING 的区别:

BLOCKED 是线程还没拿到 synchronized 的锁,在 EntryList 里等。

WAITING 是线程已经拿到过锁,但主动调用了 wait(),释放锁后进入 WaitSet 等别人唤醒。

对象头和 Mark Word

锁信息不是凭空挂在对象上的。HotSpot 虚拟机里,对象内存布局大致分三块:

  1. 对象头。
  2. 实例数据。
  3. 对齐填充。

对象头里有一块很关键,叫 Mark Word。它会根据对象状态存储不同信息,比如 hashcode、分代年龄、锁标志位、偏向线程 ID、指向锁记录的指针、指向 Monitor 的指针等。
#mermaid-svg-ZsqZIpnbOcji4d8M{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-ZsqZIpnbOcji4d8M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZsqZIpnbOcji4d8M .error-icon{fill:#552222;}#mermaid-svg-ZsqZIpnbOcji4d8M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZsqZIpnbOcji4d8M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZsqZIpnbOcji4d8M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZsqZIpnbOcji4d8M .marker.cross{stroke:#333333;}#mermaid-svg-ZsqZIpnbOcji4d8M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZsqZIpnbOcji4d8M p{margin:0;}#mermaid-svg-ZsqZIpnbOcji4d8M .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M .cluster-label text{fill:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M .cluster-label span{color:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M .cluster-label span p{background-color:transparent;}#mermaid-svg-ZsqZIpnbOcji4d8M .label text,#mermaid-svg-ZsqZIpnbOcji4d8M span{fill:#333;color:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M .node rect,#mermaid-svg-ZsqZIpnbOcji4d8M .node circle,#mermaid-svg-ZsqZIpnbOcji4d8M .node ellipse,#mermaid-svg-ZsqZIpnbOcji4d8M .node polygon,#mermaid-svg-ZsqZIpnbOcji4d8M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZsqZIpnbOcji4d8M .rough-node .label text,#mermaid-svg-ZsqZIpnbOcji4d8M .node .label text,#mermaid-svg-ZsqZIpnbOcji4d8M .image-shape .label,#mermaid-svg-ZsqZIpnbOcji4d8M .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZsqZIpnbOcji4d8M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZsqZIpnbOcji4d8M .rough-node .label,#mermaid-svg-ZsqZIpnbOcji4d8M .node .label,#mermaid-svg-ZsqZIpnbOcji4d8M .image-shape .label,#mermaid-svg-ZsqZIpnbOcji4d8M .icon-shape .label{text-align:center;}#mermaid-svg-ZsqZIpnbOcji4d8M .node.clickable{cursor:pointer;}#mermaid-svg-ZsqZIpnbOcji4d8M .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZsqZIpnbOcji4d8M .arrowheadPath{fill:#333333;}#mermaid-svg-ZsqZIpnbOcji4d8M .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZsqZIpnbOcji4d8M .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZsqZIpnbOcji4d8M .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZsqZIpnbOcji4d8M .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZsqZIpnbOcji4d8M .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZsqZIpnbOcji4d8M .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZsqZIpnbOcji4d8M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZsqZIpnbOcji4d8M .cluster text{fill:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M .cluster span{color:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M 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-ZsqZIpnbOcji4d8M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZsqZIpnbOcji4d8M rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZsqZIpnbOcji4d8M .icon-shape,#mermaid-svg-ZsqZIpnbOcji4d8M .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZsqZIpnbOcji4d8M .icon-shape p,#mermaid-svg-ZsqZIpnbOcji4d8M .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZsqZIpnbOcji4d8M .icon-shape .label rect,#mermaid-svg-ZsqZIpnbOcji4d8M .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZsqZIpnbOcji4d8M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZsqZIpnbOcji4d8M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZsqZIpnbOcji4d8M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java 对象
对象头 Header
实例数据 Instance Data
对齐填充 Padding
Mark Word
Klass Word
hashcode / age
锁标志位
线程 ID 或 Monitor 指针

当对象处于重量级锁状态时,Mark Word 里会保存指向 Monitor 的指针。

为什么要有锁升级

早期直接用 Monitor 加锁,逻辑简单,但成本比较高。因为重量级锁涉及用户态和内核态切换、线程阻塞和唤醒,竞争不激烈时有点浪费。

JDK 1.6 之后,HotSpot 引入了多种锁优化。常见说法是:

  1. 偏向锁。
  2. 轻量级锁。
  3. 重量级锁。

这三种锁对应的场景不同:

锁状态 适合场景 核心思路
偏向锁 很长时间只有同一个线程使用锁 第一次记录线程 ID,后面同线程进入尽量不做 CAS
轻量级锁 多个线程交替进入,但没有激烈竞争 用线程栈中的 Lock Record 和 CAS 竞争
重量级锁 多线程同时激烈竞争 膨胀为 Monitor,没抢到的线程阻塞

#mermaid-svg-TH0orx5MAjC6Mm8Y{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-TH0orx5MAjC6Mm8Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TH0orx5MAjC6Mm8Y .error-icon{fill:#552222;}#mermaid-svg-TH0orx5MAjC6Mm8Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TH0orx5MAjC6Mm8Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .marker.cross{stroke:#333333;}#mermaid-svg-TH0orx5MAjC6Mm8Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TH0orx5MAjC6Mm8Y p{margin:0;}#mermaid-svg-TH0orx5MAjC6Mm8Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .cluster-label text{fill:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .cluster-label span{color:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .cluster-label span p{background-color:transparent;}#mermaid-svg-TH0orx5MAjC6Mm8Y .label text,#mermaid-svg-TH0orx5MAjC6Mm8Y span{fill:#333;color:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .node rect,#mermaid-svg-TH0orx5MAjC6Mm8Y .node circle,#mermaid-svg-TH0orx5MAjC6Mm8Y .node ellipse,#mermaid-svg-TH0orx5MAjC6Mm8Y .node polygon,#mermaid-svg-TH0orx5MAjC6Mm8Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .rough-node .label text,#mermaid-svg-TH0orx5MAjC6Mm8Y .node .label text,#mermaid-svg-TH0orx5MAjC6Mm8Y .image-shape .label,#mermaid-svg-TH0orx5MAjC6Mm8Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-TH0orx5MAjC6Mm8Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .rough-node .label,#mermaid-svg-TH0orx5MAjC6Mm8Y .node .label,#mermaid-svg-TH0orx5MAjC6Mm8Y .image-shape .label,#mermaid-svg-TH0orx5MAjC6Mm8Y .icon-shape .label{text-align:center;}#mermaid-svg-TH0orx5MAjC6Mm8Y .node.clickable{cursor:pointer;}#mermaid-svg-TH0orx5MAjC6Mm8Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .arrowheadPath{fill:#333333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TH0orx5MAjC6Mm8Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TH0orx5MAjC6Mm8Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TH0orx5MAjC6Mm8Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TH0orx5MAjC6Mm8Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .cluster text{fill:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y .cluster span{color:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y 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-TH0orx5MAjC6Mm8Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TH0orx5MAjC6Mm8Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-TH0orx5MAjC6Mm8Y .icon-shape,#mermaid-svg-TH0orx5MAjC6Mm8Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TH0orx5MAjC6Mm8Y .icon-shape p,#mermaid-svg-TH0orx5MAjC6Mm8Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TH0orx5MAjC6Mm8Y .icon-shape .label rect,#mermaid-svg-TH0orx5MAjC6Mm8Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TH0orx5MAjC6Mm8Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TH0orx5MAjC6Mm8Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TH0orx5MAjC6Mm8Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



出现竞争
CAS 失败 / 竞争加剧
对象初始可偏向或无锁
是否只有同一线程反复进入
偏向锁
是否存在交替访问但竞争不激烈
轻量级锁
重量级锁 Monitor

补一个版本细节:OpenJDK 的 JEP 374 在 JDK 15 中把偏向锁默认禁用,并废弃相关命令行参数。也就是说,偏向锁仍然是理解老资料和 HotSpot 锁优化的重要概念,但在较新的 JDK 上不要默认认为它一定启用。参考:JEP 374

轻量级锁怎么加锁

轻量级锁不是"不加锁",而是用更轻的方式尝试避免线程阻塞。

大概流程是:

  1. 线程进入同步块时,在自己的栈帧里创建 Lock Record。
  2. Lock Record 指向锁对象。
  3. 线程用 CAS 尝试把对象头 Mark Word 替换成 Lock Record 的地址。
  4. CAS 成功,说明拿到轻量级锁。
  5. CAS 失败,如果是自己重入,记录重入;如果是其他线程竞争,就可能膨胀成重量级锁。

#mermaid-svg-qXWf4z14kolKKYfV{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-qXWf4z14kolKKYfV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qXWf4z14kolKKYfV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qXWf4z14kolKKYfV .error-icon{fill:#552222;}#mermaid-svg-qXWf4z14kolKKYfV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qXWf4z14kolKKYfV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qXWf4z14kolKKYfV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qXWf4z14kolKKYfV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qXWf4z14kolKKYfV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qXWf4z14kolKKYfV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qXWf4z14kolKKYfV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qXWf4z14kolKKYfV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qXWf4z14kolKKYfV .marker.cross{stroke:#333333;}#mermaid-svg-qXWf4z14kolKKYfV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qXWf4z14kolKKYfV p{margin:0;}#mermaid-svg-qXWf4z14kolKKYfV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qXWf4z14kolKKYfV .cluster-label text{fill:#333;}#mermaid-svg-qXWf4z14kolKKYfV .cluster-label span{color:#333;}#mermaid-svg-qXWf4z14kolKKYfV .cluster-label span p{background-color:transparent;}#mermaid-svg-qXWf4z14kolKKYfV .label text,#mermaid-svg-qXWf4z14kolKKYfV span{fill:#333;color:#333;}#mermaid-svg-qXWf4z14kolKKYfV .node rect,#mermaid-svg-qXWf4z14kolKKYfV .node circle,#mermaid-svg-qXWf4z14kolKKYfV .node ellipse,#mermaid-svg-qXWf4z14kolKKYfV .node polygon,#mermaid-svg-qXWf4z14kolKKYfV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qXWf4z14kolKKYfV .rough-node .label text,#mermaid-svg-qXWf4z14kolKKYfV .node .label text,#mermaid-svg-qXWf4z14kolKKYfV .image-shape .label,#mermaid-svg-qXWf4z14kolKKYfV .icon-shape .label{text-anchor:middle;}#mermaid-svg-qXWf4z14kolKKYfV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qXWf4z14kolKKYfV .rough-node .label,#mermaid-svg-qXWf4z14kolKKYfV .node .label,#mermaid-svg-qXWf4z14kolKKYfV .image-shape .label,#mermaid-svg-qXWf4z14kolKKYfV .icon-shape .label{text-align:center;}#mermaid-svg-qXWf4z14kolKKYfV .node.clickable{cursor:pointer;}#mermaid-svg-qXWf4z14kolKKYfV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qXWf4z14kolKKYfV .arrowheadPath{fill:#333333;}#mermaid-svg-qXWf4z14kolKKYfV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qXWf4z14kolKKYfV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qXWf4z14kolKKYfV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qXWf4z14kolKKYfV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qXWf4z14kolKKYfV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qXWf4z14kolKKYfV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qXWf4z14kolKKYfV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qXWf4z14kolKKYfV .cluster text{fill:#333;}#mermaid-svg-qXWf4z14kolKKYfV .cluster span{color:#333;}#mermaid-svg-qXWf4z14kolKKYfV 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-qXWf4z14kolKKYfV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qXWf4z14kolKKYfV rect.text{fill:none;stroke-width:0;}#mermaid-svg-qXWf4z14kolKKYfV .icon-shape,#mermaid-svg-qXWf4z14kolKKYfV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qXWf4z14kolKKYfV .icon-shape p,#mermaid-svg-qXWf4z14kolKKYfV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qXWf4z14kolKKYfV .icon-shape .label rect,#mermaid-svg-qXWf4z14kolKKYfV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qXWf4z14kolKKYfV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qXWf4z14kolKKYfV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qXWf4z14kolKKYfV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 成功
失败且当前线程已持有
失败且其他线程竞争
线程进入 synchronized
栈帧创建 Lock Record
Lock Record 指向锁对象
CAS 修改 Mark Word
获得轻量级锁
锁重入
膨胀为重量级锁

解锁时再用 CAS 把 Mark Word 恢复回去。如果恢复失败,也说明出现竞争,需要走重量级锁相关流程。

synchronized 支持重入

可重入的意思是:同一个线程已经拿到一把锁后,可以再次进入同一把锁保护的代码块。

java 复制代码
static final Object obj = new Object();

public static void method1() {
    synchronized (obj) {
        method2();
    }
}

public static void method2() {
    synchronized (obj) {
        // 同一个线程可以再次进入
    }
}

如果锁不可重入,method1() 里调用 method2() 就会自己等自己,直接死锁。

面试怎么答

可以按这个顺序说:

synchronized 是 Java 提供的互斥同步关键字,可以修饰方法或代码块。同步代码块在字节码层面对应 monitorentermonitorexit,同步方法通过方法访问标志实现。

它底层和对象的 Monitor 有关。Monitor 里有 Owner、EntryList、WaitSet。Owner 指向当前持锁线程,EntryList 保存没抢到锁而阻塞的线程,WaitSet 保存调用 wait() 后等待唤醒的线程。

对象头里的 Mark Word 会记录锁状态。为了降低重量级锁开销,HotSpot 曾做过偏向锁和轻量级锁优化:同一线程反复进入适合偏向锁,不同线程交替进入适合轻量级锁,竞争激烈时膨胀为重量级锁。

最后别忘了补一句,synchronized 是可重入的,退出同步代码块时会自动释放锁;如果讲到偏向锁,最好说明 JDK 15 起偏向锁默认禁用,避免被追问版本差异。

相关推荐
m0_752035631 小时前
markdown语言格式
java
布朗克1681 小时前
12 封装与构造方法
java·开发语言·封装·构造方法
z落落1 小时前
C# 抽象类(abstract)
java·开发语言·c#
大湿兄啊啊啊1 小时前
MID360S调试
java·服务器·前端
.Cnn2 小时前
Maven进阶知识点
java·maven
布朗克1682 小时前
11 面向对象思想入门
java·对象
拽着尾巴的鱼儿2 小时前
lombok.RequiredArgsConstructor bean 注入
java·lombok
铁链鞭策大师2 小时前
JavaEE之多线程
java·开发语言·java-ee