Synchronized的实现原理与应用

本文详细介绍了Java中synchronized锁的工作原理,包括其在对象头中的存储方式、锁的状态升级过程,以及偏向锁、轻量级锁和重量级锁的特点与适用场景。

1、是一个重量级锁

对于普通方法,锁的是当前对象;

对于静态同步方法,锁的是当前类的class 对象;

对于同步方法块,锁的是Synchronized括号里配置对象;

2、synchronized用的锁存在于Java对象头里面:

Java对象头包含:MarkWord,Class Metadata Address,ArrayLength;

MarkWord:存储对象的hashcode或者锁信息(默认的是 hashcode,分带年龄,锁标记位);

Class Mata Address:存储到对象类型数据的指针;

ArrayLength:数组的 长度,如果当前对象是数组;

3、锁的升级与对比:

锁一共有四种状态:无锁状态,偏向锁,轻量级锁,重量级锁:

(1)偏向锁: 在大多数情况下,锁不存在多线程竞争,但是如果是正常的锁,每次获取锁的时候都会去对象头中

进行cas操作来枷锁和解锁,偏向锁则会在对象头和栈帧中 存储偏向锁id,以后线程获取的时候只需要查看时

候有这个id,有就获取,没有对markword总偏向锁的标识设置成1,如果没有设置偏向锁标志,再进行cas;

-XX:BiasedLockingStartupDelay=0;激活的时候关闭延迟;

-XX:UseBiasedLocking=false 关闭偏向锁;

(2)轻量级锁:线程再执行同步快的时候,jvm会在当前栈帧中创建锁的记录控件并将对象的markword复制过来,然后线程尝试使用cas将对象头中的markWord替换为指向锁记录的指针,如果成功就获取锁,如果失败,表示其他线程竞争,当前线程会尝试自旋来获取锁,如果旋转之后还是不能获取锁,则会将线程转化为重量级锁。

(3)重量级锁:当此线程获取锁时,其他线程等待;

优缺点:

偏向锁:加锁和解锁不需要额外的开销,如果存在竞争,会带来额外的撤销消耗,适用于只有一个线程来访问同步块;

轻量级锁:竞争的线程不会阻塞,提高了线程的响应速度,其他线程始终得不到锁,自旋会消耗CPU,适用于追求响应时间,同步块执行速度非常快

重量级锁:线程不使用自旋,不消耗cpu,线程会阻塞,响应时间慢,追求吞吐量,同步块执行时间长;’

 

 

 

转载于:https://my.oschina.net/u/3300098/blog/1540873

<think>我们正在讨论Java中的synchronized关键字。根据引用内容,synchronized关键字用于实现线程同步,防止多个线程同时执行同步代码块,从而避免竞态条件和数据不一致问题。它的底层实现涉及到对象头中的Mark Word、锁升级过程(偏向锁、轻量级锁、重量级锁)等。 用户问题:理解Javasynchronized关键字的实现原理及其在多线程编程中的应用场景 回答结构: 1. synchronized关键字的作用 2. synchronized的底层实现原理(包括对象头、锁升级过程) 3. synchronized在多线程中的应用场景(示例) 4. 注意事项 根据引用[1][2][3][4][5],我们可以整理如下: 1. 作用:确保多个线程在访问共享资源时的协调和同步,避免竞态条件。 2. 实现原理: - 每个Java对象都有一个对象头,其中包含Mark Word(存储锁信息)。 - Mark Word在不同锁状态下存储的内容不同(无锁、偏向锁、轻量级锁、重量级锁)。 - 锁升级过程(优化): 无锁 -> 偏向锁(当只有一个线程访问时) -> 轻量级锁(当有多个线程交替访问,但未同时竞争) -> 重量级锁(当有多个线程同时竞争) 3. 应用场景:多线程环境下对共享资源的访问控制,例如: - 单例模式的双重检查锁定 - 线程安全的计数器 - 共享数据结构的操作(如Vector、Hashtable等) 4. 注意事项:synchronized是重量级锁(在升级到重量级锁时)会涉及到操作系统状态的切换,影响性能。但在JDK1.6后进行了优化,引入了偏向锁和轻量级锁,减少了性能开销。 下面详细展开:</think>### Java synchronized 关键字实现原理应用场景 #### 一、synchronized 的作用 `synchronized` 是 Java 中的**同步锁关键字**,用于解决多线程环境下的**线程安全问题**: 1. **防止竞态条件**:确保同一时刻只有一个线程访问共享资源。 2. **保证可见性**:线程对共享变量的修改对其他线程立即可见。 3. **实现有序性**:禁止指令重排序优化。 > 典型应用场景:多线程卖票系统、单例模式、共享计数器、线程安全的集合类(如 `Vector`)[^2]。 --- #### 二、底层实现原理 ##### 1. 对象头 Mark Word 每个 Java 对象在内存中包含**对象头**(Object Header),其中关键部分是 **Mark Word**(占 64 位 JVM 中的 64 位): | 锁状态 | Mark Word 存储内容 | 标志位 | |--------------|-------------------------------------|--------| | **无锁** | 哈希码 + 分代年龄 | `01` | | **偏向锁** | 线程 ID + 时间戳 + 分代年龄 | `101` | | **轻量级锁** | 指向栈中锁记录的指针 | `00` | | **重量级锁** | 指向互斥量(monitor)的指针 | `10` | ##### 2. 锁升级流程 JDK 1.6 后引入**锁升级优化**(避免直接使用重量级锁): ```mermaid graph LR A[无锁] -->|首次获取锁| B[偏向锁] B -->|发生竞争| C[轻量级锁] C -->|自旋失败| D[重量级锁] ``` - **偏向锁**:仅一个线程访问时,通过 CAS 将线程 ID 写入 Mark Word。 - **轻量级锁**:多个线程交替执行,通过 CAS 和自旋尝试获取锁。 - **重量级锁**:竞争激烈时,线程进入阻塞状态,由操作系统管理互斥量(monitor)[^3][^4]。 ##### 3. Monitor 机制 - 当升级到重量级锁时,Mark Word 指向 **ObjectMonitor** 对象(C++ 实现)。 - ObjectMonitor 包含: - `_owner`:持有锁的线程 - `_EntryList`:阻塞等待锁的线程队列 - `_WaitSet`:调用 `wait()` 的线程队列 > 同步代码块编译后生成 `monitorenter` 和 `monitorexit` 指令,由 JVM 调用 ObjectMonitor 的锁机制[^5]。 --- #### 三、应用场景示例 ##### 1. 同步代码块(显式锁对象) ```java public class Counter { private int count = 0; private final Object lock = new Object(); // 专用锁对象 public void increment() { synchronized(lock) { // 锁定特定对象 count++; } } } ``` ##### 2. 同步方法(锁当前实例) ```java public synchronized void safeWrite() { // 锁定当前实例(this) } ``` ##### 3. 静态同步方法(锁 Class 对象) ```java public static synchronized void staticSafeMethod() { // 锁定类的 Class 对象(如 Counter.class) } ``` --- #### 四、性能优化建议 1. **减小同步范围**:尽量使用同步代码块而非同步方法。 2. **避免锁嵌套**:防止死锁和性能下降。 3. **考虑替代方案**:高并发场景用 `ReentrantLock` 或 `StampedLock`。 4. **锁分离技术**:如 `ReadWriteLock` 分离读/写锁。 > 统计表明:80% 的锁在无竞争时通过偏向锁/轻量级锁可降低开销至纳秒级[^3]。 --- #### 五、常见问题解析 1. **Q: 为什么 `String` 不适合作为锁对象?** A: 字符串常量池可能被多个类共享,导致意外锁竞争。 2. **Q: `synchronized` 和 `volatile` 的区别?** A: `volatile` 仅保证可见性和有序性,不保证原子性;`synchronized` 三者均保证。 3. **Q: 锁升级是否可逆?** A: 偏向锁可撤销回无锁状态;重量级锁无法降级。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值