synchronized同步

本文详细介绍了Java中的两种锁机制——显式锁ReentrantLock和隐式锁Synchronized。Synchronized是JVM内置锁,通过监视器实现,依赖于操作系统的互斥锁。而ReentrantLock提供了更灵活的加锁方式。文章探讨了锁的膨胀过程,包括无锁、偏向锁、轻量级锁和重量级锁,并分析了锁粗化和消除的优化策略。此外,还讨论了逃逸分析及其在优化锁机制中的作用。

锁的类型


(1)显式锁:ReentrantLock ,实现juc 里Lock ,实现的是基于ReentrantLock类中的lock()和unlock(0方法

(2)隐式锁:Synchronized 加锁的机制,jvm的内置锁,它不需要手动进行加锁,jvm会自动加锁

jvm内置锁通过synchronized使用 ,通过内部的(监视器锁)来实现,基于进入进入与退出Monitor对象的实现方法与代码同步,监视起的底层依赖于操作系统的mutex lock(互斥锁)实现,是一个重要的重量级的锁,性能比较低

monitorenter
代码段
monitorexit

jvm底层monitor 的实现代码

synchronized的加锁的方式主要有

1.同步类方法 ,锁是当前类对象

  public static synchronized syn()

2.同步实例方法 ,所示当前实例对象,在spring容器中必须是single

public  synchronized syn()

3.同步代码块,锁是括号里面的对象

对象的内存结构

使用synchronized 进行对象锁的时候,必须有一个对象,来记录当前 所得状态

(1)对象头:如 hash 码 ,对象属性的所属年代,对象锁,所得状态标志,偏向锁(线程)ID ,偏向时间,数组的长度

(2)对象实际数据:创建对象时候,对象中成员变量,方法等

(3)对齐填充:对象的大小必须位8字节的整数倍

在对对象进行进行锁的时候会出现逃逸分析

逃逸:正常来说,对象创建实例后会存放到堆中,然而部分会创建在当前线程的栈中

Java1.7之后会自动配置开启逃逸分析

 关闭逃逸分析,同时调大堆空间,避免堆内GC的发生,如果有GC信息将会被打印出来
 VM运行参数:-Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
开启逃逸分析
 VM运行参数:-Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
 执行main方法后
 jps 查看进程
 jmap -histo 进程ID

出现逃逸行为的方式

 

1.同步省略。如果一个对象被发现只能从一个线程倍访问到,那么对于这个对象的操作,可以不用考虑同步

比如 synchronized(new Object){} 当前这个object 对象是动态创建的每一个对象进行使用的时候都是不同的,所以jvm对其进行了优化,不会存放到堆中而是当前的线程栈中

2.将堆分配转化位栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会发生逃逸,对象可能是栈分配的候选,而不是堆分配

3.分离对象或者标量替换。有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是CPU寄存器中。

 

在获取锁的过程中,锁会进行膨胀,无锁》偏向锁》轻量级锁》重量级锁,锁的升级是由低到高不能逆转

偏向锁:java1.6之后为了减少获取锁和释放锁(从用户态到内核态)的开销,出现了偏向锁和轻量级锁,   如果一个线程获取了锁,那么锁就进入了偏向模式,此时Mark Word 的结构也会转                  向偏向锁结构,当这个线程再次请求锁的时候,无序再做任何同步操作,既获取锁的过程,这样就会省去大量有关锁的操作,提高性能

轻量级锁:如果偏向锁失败,虚拟机不会立即升级位重量级锁,它还会尝试使用一种称为轻量级锁的优化手段(1.6之后加入),此时Mark Word 的结构也会变为轻量级锁的结构。轻量级锁适应的场景是线程交  替执行同步块的场合,如果存在同一时间访问同一锁的场合,如果没有获取锁不会立即升为重量级的锁,而是进行锁的自旋,为了避免线程真实地在操作系统层面挂起,因为从用户态到内核态的开销比较大,就是空转,因为每个线程占用CPU的时间片都会很短,在进行自选之后如果不能获取锁就会升级为重量级锁

锁的粗化:

是jvm对锁的一种优化方式

这是另外一种锁的优化方式,java 虚拟机在JIT编译的时候(可以简单理解为当某段代码即将第一次被执行时进行编译,又称及时编译),通过对运行上下文扫描,去除不可能存在共享资源竞争的锁,通过这种方式可以节省毫无意义的请求锁的时间。

如 StringBuffer sb=new StringBuffer();

   sb.append("a")

  sb.append("b")

因为StrinbBuffer是线程安全的每次进行append 里面有synchronized同步方法,但是进行连续添加的时候,虚拟机不会给每一个添加锁而是 对其进行优化

synchronized(this){

  sb.append("a")

  sb.append("b")

}

锁的消除.

当 synchronized(new Object){}

jvm对其进行优化的时候不会对其进行加锁,因为当前的对象只会被当前线程所使用,其中的方法不会加锁

 

<think> 嗯,用户需要了解synchronized同步锁的原理、使用方法和应用场景。从用户提问方式和引用资料来看,ta应该是Java开发者,可能正在处理并发问题或准备面试。 用户引用了三篇技术文章,说明ta对底层实现特别关注。第一篇提到JDK1.6的优化,第二篇讲基础用法,第三篇涉及字节码指令。需要把这些信息整合成体系化的回答。 关于原理部分,要重点说明三个层次:字节码层的monitor指令、JVM层的Monitor对象关联、JDK优化措施(偏向锁/轻量级锁这些)。用户上次问题里提到“性能超越Lock”,这点需要强调优化效果,但也要说明适用场景限制。 使用方法方面,用户给的引用2展示了基础写法,可以延伸出三种使用场景的对比:实例方法锁、静态方法锁、代码块锁。要特别注意代码块锁的灵活性,这是实际开发中最常用的。 应用场景需要典型且具体。比如转账操作要锁账户对象,配置更新要锁Class对象,订单处理要锁实例。这些例子能让用户立即理解如何落地。 用户可能隐含的性能疑惑要主动解答:虽然优化后性能提升,但长时间等待的场景还是该用Lock。这个补充能避免ta踩坑。 最后生成的问题要覆盖知识纵深(CAS对比)、横向扩展(并发工具)、实践痛点(死锁排查)。特别是死锁问题,开发中遇到时往往很头疼。 </think> 以下是关于 `synchronized` 同步锁的全面解析,涵盖原理、使用方法及应用场景: --- ### **一、实现原理** 1. **字节码指令** 通过 `monitorenter`(加锁)和 `monitorexit`(释放锁)指令实现: - 线程执行 `monitorenter` 时尝试获取对象的 **Monitor 所有权**; - 成功获取后,Monitor 进入锁定状态; - 执行 `monitorexit` 后释放 Monitor [^3]。 2. **Monitor 机制** - 每个 Java 对象关联一个 Monitor 对象(由 JVM 创建); - Monitor 包含三个关键字段: - `_owner`:持有锁的线程; - `_EntryList`:阻塞等待锁的线程队列; - `_WaitSet`:调用 `wait()` 后进入等待状态的线程队列。 3. **JDK 1.6 后的优化** - **锁升级机制**(显著提升性能): ```mermaid graph LR A[无锁] -->|首次竞争| B[偏向锁] B -->|多线程竞争| C[轻量级锁 CAS] C -->|竞争加剧| D[重量级锁 Monitor] ``` - 偏向锁:减少无竞争时的开销; - 轻量级锁:通过自旋(CAS)避免线程阻塞; - 重量级锁:竞争激烈时转为操作系统级互斥锁 [^1]。 --- ### **二、使用方法** #### **1. 同步代码块** ```java public void func() { synchronized (lockObject) { // 锁对象可以是任意实例或 Class 对象 // 临界区代码 } } ``` - **锁对象选择**: - 实例对象锁:`synchronized(this)` 或 `synchronized(new Object())`; - 类对象锁:`synchronized(MyClass.class)`。 #### **2. 同步方法** ```java // 实例方法锁:锁住当前对象实例 public synchronized void instanceMethod() { ... } // 静态方法锁:锁住类的 Class 对象 public static synchronized void staticMethod() { ... } ``` --- ### **三、应用场景** 1. **实例数据保护** ```java public class BankAccount { private double balance; public synchronized void transfer(double amount) { balance += amount; // 保证转账操作原子性 } } ``` 2. **静态资源共享** ```java public class ConfigLoader { private static Map<String, String> configCache; public static synchronized void reloadConfig() { // 保证全局配置加载线程安全 } } ``` 3. **双重检查锁定(DCL)** ```java public class Singleton { private volatile static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` --- ### **四、注意事项** 1. **避免死锁**: 确保多个线程的锁获取顺序一致(如按对象哈希值排序)。 2. **锁粒度优化**: - 高并发场景拆分锁:如 `ConcurrentHashMap` 分段锁; - 读写分离场景:优先使用 `ReentrantReadWriteLock`。 3. **性能权衡**: - 低竞争时:`synchronized` 因 JIT 优化性能接近 Lock; - 高竞争时:`ReentrantLock` 的 `tryLock()` 和公平锁策略更灵活。 > **💎 核心总结**:`synchronized` 是 JVM 层实现的互斥锁,通过 Monitor 和锁升级机制平衡安全性与性能,适用于大部分线程同步场景。但在需要超时锁、可中断锁或公平锁时,需改用 `Lock` 接口实现 [^1][^2]。 --- **相关问题** 1. `synchronized` 和 `ReentrantLock` 在哪些场景下性能差异显著? 2. 如何通过 Jstack 诊断 `synchronized` 导致的线程阻塞? 3. 偏向锁的撤销流程在 JVM 中如何触发? 4. 为什么双重检查锁定需要 `volatile` 关键字?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值