当前位置: 代码网 > it编程>编程语言>Java > Java synchronized从使用到底层锁升级机制详解

Java synchronized从使用到底层锁升级机制详解

2026年03月25日 Java 我要评论
在java并发编程中,synchronized是最基础也最核心的锁机制——它使用简单(加个关键字就能保证线程安全),但底层原理却藏着大量面试高频考点:只会用synchroniz

在java并发编程中,synchronized是最基础也最核心的锁机制——它使用简单(加个关键字就能保证线程安全),但底层原理却藏着大量面试高频考点:

  • 只会用synchronized修饰方法,说不清楚“实例锁”和“类锁”的区别;
  • 被追问“synchronized底层怎么实现?”,只知道“monitor”却讲不出细节;
  • 分不清“偏向锁、轻量级锁、重量级锁”的升级逻辑,答不出jdk 1.6的优化点;
  • 面试被问“为什么synchronized是可重入的?”“高并发下偏向锁要不要关闭?”当场卡壳。

本文从使用场景字节码原理底层结构锁升级流程面试考点层层拆解,结合图解代码案例源码片段,彻底讲透synchronized的核心逻辑:
✅ 3种使用方式+字节码层面解析,分清实例锁/类锁;
✅ 图解对象头(mark word)结构,看懂锁状态的存储逻辑;
✅ 动态拆解“无锁→偏向锁→轻量级锁→重量级锁”升级全过程;
✅ jdk 1.6锁优化:自适应自旋、锁消除、锁粗化(面试加分点);
✅ 10+高频面试题标准答案(直接背)。

📌 核心一句话
synchronized基于对象监视器(monitor)实现,jdk 1.6为解决性能问题引入“锁升级”机制,从无锁逐步升级为偏向锁、轻量级锁、重量级锁,仅在高竞争场景下才会触发重量级锁(依赖os互斥量),大幅提升低竞争场景的性能。

📌 面试金句先记牢

  • synchronized的三种用法:实例锁(this)、类锁(class对象)、代码块锁(任意对象);
  • 锁升级是不可逆的单向过程(偏向锁→轻量级锁→重量级锁),核心依赖对象头mark word存储锁状态;
  • jdk 1.6优化后,synchronized不再是“重量级锁”的代名词,低竞争场景下性能接近cas;
  • synchronized的可重入性依赖monitor的计数器机制,每加锁一次计数器+1,解锁一次-1,为0时释放锁。
  • 字节码层面通过monitorentermonitorexit指令实现;

一、基础篇:synchronized的3种使用方式(避坑版)

synchronized的使用看似简单,但90%的开发者会混淆“实例锁”和“类锁”,导致线程安全问题。先从使用方式入手,夯实基础。

1.1 修饰实例方法(实例锁)

锁对象是当前实例(this),不同实例之间的锁互不干扰。

public class syncinstancemethod {
    // 实例锁:锁对象是this
    public synchronized void dosomething() {
        system.out.println("实例方法锁:" + thread.currentthread().getname());
        try {
            thread.sleep(1000); // 模拟耗时操作
        } catch (interruptedexception e) {
            thread.currentthread().interrupt();
        }
    }
    public static void main(string[] args) {
        syncinstancemethod instance1 = new syncinstancemethod();
        syncinstancemethod instance2 = new syncinstancemethod();
        // 线程1:调用instance1的同步方法(锁instance1)
        new thread(() -> instance1.dosomething(), "线程1").start();
        // 线程2:调用instance2的同步方法(锁instance2)→ 不会阻塞
        new thread(() -> instance2.dosomething(), "线程2").start();
    }
}

结果:线程1和线程2同时执行(不同实例,锁不互斥)。

1.2 修饰静态方法(类锁)

锁对象是类的class对象(如syncstaticmethod.class),所有实例共享同一把锁。

public class syncstaticmethod {
    // 类锁:锁对象是syncstaticmethod.class
    public static synchronized void dostaticsomething() {
        system.out.println("静态方法锁:" + thread.currentthread().getname());
        try {
            thread.sleep(1000);
        } catch (interruptedexception e) {
            thread.currentthread().interrupt();
        }
    }
    public static void main(string[] args) {
        syncstaticmethod instance1 = new syncstaticmethod();
        syncstaticmethod instance2 = new syncstaticmethod();
        // 线程1:调用静态方法(锁class对象)
        new thread(() -> instance1.dostaticsomething(), "线程1").start();
        // 线程2:调用静态方法(同一把类锁)→ 阻塞
        new thread(() -> instance2.dostaticsomething(), "线程2").start();
    }
}

结果:线程2等待线程1执行完后才执行(类锁全局唯一)。

1.3 修饰代码块(自定义锁对象)

灵活指定锁对象,是最常用的方式(缩小锁范围,提升性能)。

public class synccodeblock {
    // 自定义锁对象(推荐使用final,避免锁对象被修改)
    private final object lock = new object();
    // 类锁的代码块写法
    private static final object classlock = new object();
    public void doblocksomething() {
        // 锁自定义对象
        synchronized (lock) {
            system.out.println("代码块锁(实例):" + thread.currentthread().getname());
            try {
                thread.sleep(1000);
            } catch (interruptedexception e) {
                thread.currentthread().interrupt();
            }
        }
    }
    public static void dostaticblocksomething() {
        // 锁class对象(等价于静态方法锁)
        synchronized (synccodeblock.class) {
            system.out.println("代码块锁(类):" + thread.currentthread().getname());
            try {
                thread.sleep(1000);
            } catch (interruptedexception e) {
                thread.currentthread().interrupt();
            }
        }
    }
    public static void main(string[] args) {
        synccodeblock instance = new synccodeblock();
        // 同一实例的代码块锁互斥
        new thread(() -> instance.doblocksomething(), "线程1").start();
        new thread(() -> instance.doblocksomething(), "线程2").start();
    }
}

1.4 核心避坑点

错误用法问题本质正确做法
锁字符串常量(如synchronized ("lock")字符串常量池复用,导致不同业务共享同一把锁使用自定义final object作为锁对象
锁局部变量(如synchronized (new object())每次创建新对象,锁失效(线程不互斥)锁成员变量或类变量
实例锁和类锁混用两者是不同锁对象,无法保证线程安全明确锁粒度,统一使用实例锁或类锁

二、原理篇:字节码+monitor,看懂synchronized的底层实现

要理解锁升级,先搞懂synchronized最基础的实现逻辑——字节码指令和monitor机制。

2.1 字节码层面:monitorenter/monitorexit指令

编译后的synchronized代码块会生成monitorentermonitorexit指令,修饰方法则会在方法表中标记acc_synchronized

代码示例

public class syncbytecode {
    private final object lock = new object();
    public void test() {
        synchronized (lock) {
            system.out.println("synchronized代码块");
        }
    }
}

核心字节码(关键部分)

执行:

javap -c syncbytecode.class

字节码信息如下:

// synchronized (lock) 对应的字节码
0 aload_0
1 getfield      #2                  // 获取lock对象
4 dup
5 astore_1
6 monitorenter  // 进入monitor,获取锁
7 getstatic     #3                  // system.out
10 ldc           #4                  // 字符串"synchonized代码块"
12 invokevirtual #5                  // 执行println
15 aload_1
16 monitorexit   // 退出monitor,释放锁
17 goto          25
20 astore_2
21 aload_1
22 monitorexit   // 异常时的monitorexit(保证锁释放)
23 aload_2
24 athrow
25 return

指令解析

  • monitorenter:尝试获取对象的monitor所有权,成功则计数器+1,失败则线程阻塞;
  • monitorexit:释放monitor所有权,计数器-1,当计数器为0时,完全释放锁;
  • 编译器会生成两个monitorexit:一个正常退出,一个异常退出(保证锁最终释放,避免死锁)。

2.2 monitor(对象监视器):synchronized的核心依赖

每个java对象都关联一个monitor(c++实现,objectmonitor类),其核心结构如下:

  • _owner:当前持有锁的线程,初始为null;
  • _entrylist:未获取锁的线程进入该队列,处于blocked状态;
  • _waitset:调用wait()的线程进入该队列,处于waiting状态;
  • _count:可重入计数器,初始为0,每加锁一次+1,解锁一次-1。

2.3 基础实现流程

  1. 线程执行monitorenter时,尝试将monitor的_owner设为当前线程:
    • 成功:_count+1,执行同步代码块;
    • 失败:进入_entrylist阻塞,等待锁释放。
  2. 线程执行monitorexit时,_count-1:
    • _count=0:释放锁(_owner设为null),唤醒_entrylist中的线程竞争锁;
    • _count>0:仅减少计数器(可重入特性)。

三、核心篇:对象头mark word与锁状态存储

锁升级的核心是对象头(object header),其中的mark word字段存储了锁状态、线程id等关键信息,先看懂mark word的结构,才能理解锁升级。

3.1 java对象的内存布局

每个java对象在内存中分为3部分:

┌─────────────────────────────────────────────────────────┐
│                    object header (对象头)               │
│ ├─────────────────────────────────────────────────────┤ │
│ │ mark word (标记字段):存储锁状态、哈希值、线程id等  │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ klass pointer (类型指针):指向类的class对象         │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ instance data (实例数据):存储对象的成员变量            │
├─────────────────────────────────────────────────────────┤
│ padding (对齐填充):保证对象大小为8字节的整数倍         │
└─────────────────────────────────────────────────────────┘
  • mark word:核心字段,长度为32位(32位jvm)或64位(64位jvm),动态存储不同信息(根据锁状态);
  • klass pointer:默认占4字节(32位)/8字节(64位),指向对象的类元数据;
  • padding:仅为内存对齐,无业务意义。

3.2 mark word的详细布局(64位vm为例)

mark word在不同锁状态下存储的内容完全不同 :

锁状态25bit31bit1bit4bit1bit(偏向锁)2bit(锁标志)
无锁unusedhashcodeunused分代年龄001
偏向锁threadid(54bit)epoch分代年龄101
轻量级锁指向栈中锁记录(lock record)的指针(62bit)00
重量级锁指向monitor对象的指针(62bit)10
gc标记空(不存信息)11

💡 关键:

  1. 锁状态由最后2位标识:01=无锁/偏向锁,00=轻量级锁,10=重量级锁,11=gc标记;
  2. 偏向锁和无锁的锁状态都是01,通过是否偏向锁位(第6位) 区分;
  3. mark word的动态复用是锁升级的核心基础。

3.3 图解:mark word状态切换

┌─────────────────────────────────────────────────────────┐
│                      无锁(001)                          │
│  ┌─────────────────────────────────────┐               │
│  │      hashcode(25/31bit)│  age  │ 0│ 01 │
│  └─────────────────────────────────────┘               │
│                         ↓ 第一个线程获取锁               │
├─────────────────────────────────────────────────────────┤
│                    偏向锁(101)                          │
│  ┌─────────────────────────────────────┐               │
│  │      threadid(54bit)│epoch │age│ 1│ 01 │
│  └─────────────────────────────────────┘               │
│                         ↓ 第二个线程竞争                 │
├─────────────────────────────────────────────────────────┤
│                  轻量级锁(00)                           │
│  ┌─────────────────────────────────────┐               │
│  │     指向lock record的指针(62bit)  │ 00 │
│  └─────────────────────────────────────┘               │
│                         ↓ 自旋失败/竞争加剧              │
├─────────────────────────────────────────────────────────┤
│                  重量级锁(10)                           │
│  ┌─────────────────────────────────────┐               │
│  │     指向monitor对象的指针(62bit)  │ 10 │
│  └─────────────────────────────────────┘               │
└─────────────────────────────────────────────────────────┘

四、进阶篇:锁升级全过程(无锁→偏向锁→轻量级锁→重量级锁)

jdk 1.5及之前,synchronized直接使用重量级锁(依赖os互斥量mutex),每次加锁解锁都需要从用户态切换到内核态,开销巨大,性能极差;

jdk 1.6引入“锁升级”机制,仅在高竞争场景下才触发重量级锁,大幅提升性能。

核心前提

  • 锁升级是单向不可逆的:无锁 → 偏向锁 → 轻量级锁 → 重量级锁;
  • 偏向锁默认开启(jdk 1.6+),启动后有4秒延迟(可通过jvm参数关闭);
  • 锁升级的触发条件是线程竞争:无竞争→偏向锁,轻度竞争→轻量级锁,重度竞争→重量级锁。

4.1 第一步:无锁状态

  • 对象刚创建时,mark word存储哈希值、分代年龄,是否偏向锁位=0,锁状态=01;
  • 无任何线程竞争,无需加锁。

4.2 第二步:偏向锁(无竞争场景)

触发条件

第一个线程获取锁,且无其他线程竞争。

核心逻辑(消除cas,提升性能)

  1. 线程获取锁时,jvm将mark word的是否偏向锁位设为1,并把当前线程id写入mark word;
  2. 该线程后续再次获取锁时,只需检查mark word中的线程id是否为自身:
    • 是:直接进入同步代码块(无需cas,无需操作系统介入);
    • 否:触发偏向锁撤销/升级。
  3. 偏向锁的释放不主动释放,仅在其他线程竞争时才会撤销。

偏向锁的优势

消除了轻量级锁的cas操作,是jdk 1.6对synchronized最核心的优化——低竞争场景下,偏向锁的性能接近无锁。

4.3 第三步:轻量级锁(轻度竞争)

触发条件

有其他线程竞争偏向锁,且当前持有锁的线程仍在执行(未释放)。

核心逻辑(cas自旋,避免os阻塞)

  1. 竞争线程尝试通过cas将mark word中的指针指向自己的栈帧中的“锁记录(lock record)”:
    • 成功:获取轻量级锁,锁状态改为00;
    • 失败:自旋(循环重试cas),自旋次数达到阈值后升级为重量级锁。
  2. 轻量级锁的释放:通过cas将mark word恢复为无锁状态,成功则释放锁,失败则说明有竞争,升级为重量级锁。

自适应自旋(jdk 1.6优化)

  • 自旋次数不是固定值,而是根据“前一次自旋是否成功”动态调整:
    • 若前一次自旋成功,本次自旋次数增加(认为大概率再次成功);
    • 若前一次自旋失败,本次自旋次数减少(甚至直接升级为重量级锁)。
  • 优势:避免固定自旋次数导致的cpu浪费,适配不同竞争场景。

4.4 第四步:重量级锁(重度竞争)

触发条件

轻量级锁自旋失败,或多个线程同时竞争锁。

核心逻辑(依赖os互斥量,性能最差)

  1. 线程获取锁失败后,放弃自旋,进入monitor的_entrylist队列,由操作系统进行阻塞(从用户态切换到内核态);
  2. 持有锁的线程释放锁时,操作系统唤醒_entrylist中的线程,重新竞争锁;
  3. 重量级锁的锁状态为10,mark word存储指向monitor的指针。

性能瓶颈

用户态→内核态的切换成本极高,这也是jdk 1.5之前synchronized被诟病“重量级”的根本原因。

4.5 锁升级流程图解

┌─────────────┐
│    无锁      │
│  标志位:01   │
│  是否偏向:0  │
└──────┬──────┘
       │ 第一个线程获取锁
       ▼
┌─────────────┐
│   偏向锁     │
│  标志位:01   │
│  是否偏向:1  │
│  存储:线程id │
└──────┬──────┘
       │ 另一个线程竞争
       ▼
┌─────────────┐
│  轻量级锁    │
│  标志位:00   │
│  存储:锁记录 │←───自旋等待
└──────┬──────┘
       │ 竞争加剧/自旋失败
       ▼
┌─────────────┐
│  重量级锁    │
│  标志位:10   │
│  存储:monitor│
└─────────────┘

4.6 三种锁对比

锁类型优点缺点适用场景
偏向锁加锁解锁无额外开销(仅检查)如果有竞争,撤销偏向锁有代价单线程访问同步块
轻量级锁用cas代替互斥量,性能好自旋会占用cpu线程交替执行同步块
重量级锁线程阻塞不占用cpu上下文切换开销大多线程同时竞争

五、优化篇:jdk 1.6的其他锁优化

除了锁升级,jdk 1.6还引入了锁消除、锁粗化等优化,进一步提升synchronized的性能。

5.1 锁消除(lock elimination)

核心逻辑

jvm的即时编译器(jit)检测到某些锁对象是“局部变量”,且不会被多线程访问,直接消除锁。

示例

public string concat(string a, string b) {
    // stringbuffer的append方法是同步的,但sb是局部变量,无多线程访问
    stringbuffer sb = new stringbuffer();
    sb.append(a).append(b);
    return sb.tostring();
}

jit编译时会消除stringbuffer的同步锁,等价于:

public string concat(string a, string b) {
    stringbuilder sb = new stringbuilder(); // 非同步
    sb.append(a).append(b);
    return sb.tostring();
}

5.2 锁粗化(lock coarsening)

核心逻辑

将多个连续的细粒度锁合并为一个粗粒度锁,减少锁的获取/释放次数。

示例

public void loopadd(string str) {
    stringbuffer sb = new stringbuffer();
    for (int i = 0; i < 1000; i++) {
        sb.append(str); // 每次append都加锁/解锁
    }
}

jit编译时会将锁粗化,等价于:

public void loopadd(string str) {
    stringbuffer sb = new stringbuffer();
    synchronized (sb) { // 一次加锁,覆盖整个循环
        for (int i = 0; i < 1000; i++) {
            sb.append(str);
        }
    }
}

5.3 偏向锁的启用/关闭(jvm参数)

参数作用
-xx:+usebiasedlocking开启偏向锁(默认开启)
-xx:biasedlockingstartupdelay=0关闭偏向锁4秒启动延迟
-xx:-usebiasedlocking关闭偏向锁

💡 实战建议:高并发场景下(如秒杀、高频读写),偏向锁会频繁触发撤销/升级,建议关闭(-xx:-usebiasedlocking),直接使用轻量级锁。

六、深度篇:synchronized的可重入性原理

synchronized是可重入锁(同一线程可多次获取同一把锁),核心依赖monitor的_count计数器:

示例代码

public class syncreentrant {
    public synchronized void method1() {
        system.out.println("method1");
        method2(); // 同一线程再次获取锁,可重入
    }
    public synchronized void method2() {
        system.out.println("method2");
    }
    public static void main(string[] args) {
        new syncreentrant().method1();
    }
}

可重入流程

  1. 线程调用method1(),获取锁,monitor的_count=1;
  2. 线程调用method2(),再次获取同一把锁,_count=2;
  3. method2()执行完,释放锁,_count=1;
  4. method1()执行完,释放锁,_count=0,完全释放锁。

核心价值

避免同一线程多次获取同一把锁时出现死锁(如递归调用同步方法)。

七、面试高频真题(标准答案直接背)

7.1 基础必答

q1:synchronized的三种使用方式及区别?

答案

  1. 修饰实例方法:锁对象是this(当前实例),不同实例锁互不干扰;
  2. 修饰静态方法:锁对象是类的class对象,所有实例共享同一把锁;
  3. 修饰代码块:自定义锁对象(如final object),可缩小锁范围,提升性能。
    核心区别是锁对象不同,导致锁的粒度和作用域不同。

q2:synchronized底层如何实现?

答案

  1. 字节码层面:代码块生成monitorenter/monitorexit指令,方法修饰acc_synchronized标记;
  2. 底层依赖对象监视器(monitor):每个对象关联一个monitor,包含owner、entrylist、waitset、计数器;
  3. jdk 1.6+引入锁升级机制:无锁→偏向锁→轻量级锁→重量级锁,仅重度竞争时使用重量级锁(os互斥量),大幅提升性能。

q3:synchronized的锁升级过程?

答案

  1. 无锁:对象创建时的初始状态,mark word存储哈希值;
  2. 偏向锁:第一个线程获取锁,mark word写入线程id,后续该线程无需cas直接获取锁;
  3. 轻量级锁:有线程竞争偏向锁,通过cas自旋获取锁,自适应自旋失败后升级;
  4. 重量级锁:轻量级锁自旋失败,线程进入monitor的entrylist阻塞,依赖os互斥量实现。
    锁升级是单向不可逆的,仅能从低级别向高级别升级。

7.2 深度追问

q4:偏向锁、轻量级锁、重量级锁的性能对比?

答案

  • 偏向锁:性能最优,无cas、无os调用,仅检查线程id;
  • 轻量级锁:性能次之,cas自旋(用户态),无os阻塞;
  • 重量级锁:性能最差,用户态→内核态切换,线程阻塞/唤醒成本高。
    核心原则:尽量让锁停留在偏向锁/轻量级锁阶段,避免升级为重量级锁。

q5:为什么偏向锁默认有4秒延迟?

答案
jvm启动时会加载大量类,多线程竞争激烈,偏向锁会频繁撤销/升级,反而降低性能。4秒延迟是为了等jvm启动完成后,再开启偏向锁,适配低竞争场景。

q6:synchronized和volatile的区别?

答案

维度synchronizedvolatile
原子性支持(保证代码块原子执行)不支持(仅保证可见性)
可见性支持(释放锁时刷新内存)支持(禁止指令重排+内存刷新)
有序性支持(通过锁保证执行顺序)支持(禁止指令重排)
锁特性可重入锁,有锁升级机制无锁,仅修饰变量
适用场景代码块/方法的线程安全变量的可见性(如状态标记)

q7:高并发场景下,偏向锁是否需要关闭?为什么?

答案
需要关闭。原因:

  1. 高并发场景下,偏向锁的线程id会频繁被竞争线程修改,触发偏向锁撤销;
  2. 频繁的撤销/升级会产生额外开销,反而比直接使用轻量级锁更慢;
  3. 关闭偏向锁后,直接进入轻量级锁阶段,避免撤销开销。

q8:synchronized的可重入性原理是什么?

答案
依赖monitor的计数器机制:

  1. 线程首次获取锁,monitor的计数器_count=1;
  2. 同一线程再次获取锁,计数器+1;
  3. 线程释放锁,计数器-1;
  4. 计数器为0时,完全释放锁,其他线程可竞争。
    该机制避免了同一线程多次获取同一把锁导致的死锁。

7.3 实战场景题

q9:如何优化synchronized的性能?

答案

  1. 缩小锁范围:使用代码块锁替代方法锁,仅锁定核心临界区;
  2. 降低锁粒度:如concurrenthashmap的分段锁(jdk 1.7)、longadder的分段累加;
  3. 关闭偏向锁:高并发场景下通过jvm参数关闭偏向锁;
  4. 避免锁竞争:如读写分离、使用无锁数据结构(atomic系列);
  5. 利用jdk优化:依赖jit的锁消除、锁粗化。

q10:synchronized和reentrantlock的区别?

答案

维度synchronizedreentrantlock
底层实现jvm层面(monitor)jdk层面(aqs)
锁类型非公平锁(默认)可公平/非公平锁
解锁方式自动解锁(monitorexit)手动解锁(必须finally中释放)
功能扩展无(仅基础锁功能)支持中断、超时获取锁、条件变量
性能jdk 1.6+优化后,低竞争接近reentrantlock高竞争场景性能更优
可重入性支持支持

八、常见误区与避坑指南

8.1 典型误区

❌ 误区1:synchronized是重量级锁,性能差

纠正:jdk 1.6+引入锁升级后,低竞争场景下synchronized的性能接近cas,仅重度竞争时才会升级为重量级锁。

❌ 误区2:锁升级是可逆的

纠正:锁升级是单向不可逆的,一旦升级为重量级锁,不会降级为轻量级锁/偏向锁。

❌ 误区3:偏向锁一定提升性能

纠正:高并发场景下,偏向锁的撤销开销会抵消其优势,建议关闭。

❌ 误区4:synchronized能保证原子性,所以无需考虑可见性

纠正:synchronized同时保证原子性、可见性、有序性——释放锁时会将变量刷新到主内存,获取锁时会从主内存加载最新值。

8.2 编码规范

  1. 缩小锁范围:仅锁定临界区代码,避免整个方法加锁;
  2. 选择合适的锁对象:使用final object作为锁对象,避免锁字符串常量/局部变量;
  3. 避免锁嵌套:减少死锁风险,如必须嵌套,保证锁的获取顺序一致;
  4. 高并发关闭偏向锁:通过-xx:-usebiasedlocking关闭,提升性能;
  5. 结合其他并发工具:如读写分离场景使用reentrantreadwritelock,替代synchronized

总结

1. 核心知识点速记口诀

sync有三种,实例静态块,
对象头mark,锁状态存,
无锁到偏向,轻量到重量,
升级不可逆,性能逐下降,
jdk1.6优化,自旋锁消除,
可重入计数,解锁要记清。

2. 核心要点回顾

  1. synchronized有三种使用方式,核心区别是锁对象不同(实例/class/自定义对象);
  2. 底层依赖monitor实现,jdk 1.6引入锁升级机制(无锁→偏向锁→轻量级锁→重量级锁),大幅提升性能;
  3. 偏向锁消除cas开销,轻量级锁依赖自旋cas,重量级锁依赖os互斥量;
  4. jdk 1.6的其他优化:自适应自旋、锁消除、锁粗化;
  5. synchronized是可重入锁,依赖monitor计数器实现,同时保证原子性、可见性、有序性。

3. 实战建议

  • 低竞争场景:依赖默认的偏向锁,无需额外优化;
  • 中竞争场景:缩小锁范围,利用轻量级锁的自旋优势;
  • 高竞争场景:关闭偏向锁,结合分段锁/无锁结构(如atomic系列),避免升级为重量级锁。

写在最后

synchronized是java并发的基础,也是面试中区分“基础开发者”和“资深开发者”的关键。很多开发者只会用synchronized保证线程安全,但说不清楚锁升级、mark word、monitor等底层原理,最终在面试中失利。

希望这篇文章能帮你吃透synchronized的核心逻辑,不仅能背出面试答案,更能理解底层原理,在实际开发中写出高性能、无坑的并发代码。

到此这篇关于java synchronized从使用到底层锁升级机制详解的文章就介绍到这了,更多相关java synchronized底层锁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com