当前位置: 首页 > news >正文

Java多线程知识小结:Synchronized

在Java中,synchronized 关键字是实现线程同步的核心工具,用于保证同一时刻只有一个线程可以执行被修饰的代码块或方法。以下从基本原理、锁升级过程、应用场景及优化建议四个维度详细解析:

一、基本原理

1. 同步的对象

synchronized 锁的是对象而非代码:

  • 修饰实例方法:锁的是当前实例对象(this);
  • 修饰静态方法:锁的是类的Class对象(如MyClass.class);
  • 修饰代码块:锁的是括号中的对象(如synchronized(obj))。
2. 底层实现
  • JVM层面:通过对象头中的Mark Word和**Monitor(监视器)**实现。
    对象头的Mark Word包含锁状态位(如偏向锁、轻量级锁、重量级锁)和指向Monitor的指针。
  • 字节码层面
    • 同步方法:通过ACC_SYNCHRONIZED标志位标识;
    • 同步代码块:通过monitorentermonitorexit指令实现。
3. 互斥锁语义
  • 线程进入同步块前需获取Monitor的所有权(锁),退出时释放锁;
  • 未获取到锁的线程会被阻塞,进入Monitor的等待队列。

二、锁升级的具体过程

JDK 6之后,synchronized 锁机制进行了优化,引入锁升级(偏向锁 → 轻量级锁 → 重量级锁)以减少性能开销。

1. 偏向锁(Biased Locking)
  • 适用场景:单线程环境,无锁竞争。
  • 原理
    1. 首次线程访问同步块时,Mark Word存储该线程ID(偏向状态);
    2. 后续该线程再次进入同步块时,无需CAS操作,直接获取锁;
    3. 若其他线程尝试竞争锁,偏向锁撤销,升级为轻量级锁。
  • 优点:无CAS操作,仅首次获取锁时有少量开销。
  • 缺点:存在锁撤销(偏向锁撤销需STW),若存在多线程竞争,反而增加开销。
2. 轻量级锁(Lightweight Locking)
  • 适用场景:多线程交替执行同步块(无实际竞争)。
  • 原理
    1. 线程进入同步块时,JVM在当前线程栈帧中创建锁记录(Lock Record)
    2. 通过CAS将对象头的Mark Word复制到锁记录,并将Mark Word指向锁记录地址;
    3. 若CAS成功,获取轻量级锁;若失败,说明存在竞争,锁膨胀为重量级锁。
  • 优点:竞争不激烈时,避免线程阻塞,减少用户态/内核态切换。
  • 缺点:竞争激烈时,频繁CAS导致性能下降。
3. 重量级锁(Heavyweight Lock)
  • 适用场景:多线程同时竞争锁。
  • 原理
    1. 锁膨胀后,Mark Word指向操作系统的Monitor对象
    2. 未获取到锁的线程被阻塞(进入内核态),放入Monitor的等待队列;
    3. 锁释放时,唤醒等待队列中的线程(需从内核态转回用户态)。
  • 缺点:线程阻塞/唤醒涉及用户态与内核态切换,性能开销大。
4. 锁升级流程图
无锁状态 → 偏向锁(单线程) → 轻量级锁(多线程交替) → 重量级锁(多线程竞争)
5. 锁升级的触发条件
  • 偏向锁撤销:当其他线程尝试访问偏向锁对象时,JVM在安全点(Safepoint)撤销偏向锁;
  • 轻量级锁膨胀:线程尝试CAS获取轻量级锁失败时,锁膨胀为重量级锁;
  • 批量重偏向/撤销:当一个类的对象频繁发生偏向锁撤销时,JVM会对该类的对象批量重偏向或撤销。

三、应用场景

1. 保护共享资源
  • 示例:多线程操作同一个计数器:
    public class Counter {private int count = 0;// 同步方法,锁的是thispublic synchronized void increment() {count++;}
    }
    
2. 实现原子操作
  • 示例:双重检查锁定(DCL)实现单例模式:
    public class Singleton {private static volatile Singleton instance; // 必须用volatile禁止指令重排public static Singleton getInstance() {if (instance == null) { // 第一次检查synchronized (Singleton.class) { // 锁的是Class对象if (instance == null) { // 第二次检查instance = new Singleton();}}}return instance;}
    }
    
3. 保证复合操作的原子性
  • 示例:检查再执行(Check-Then-Act)操作:
    public class Inventory {private Map<String, Integer> stock = new HashMap<>();// 复合操作:先检查库存,再扣减public synchronized boolean decreaseStock(String product, int amount) {if (stock.getOrDefault(product, 0) >= amount) {stock.put(product, stock.get(product) - amount);return true;}return false;}
    }
    
4. 线程间协作(wait/notify机制)
  • 示例:生产者-消费者模型:
    public class ProducerConsumer {private final Queue<Integer> queue = new LinkedList<>();private final int CAPACITY = 10;// 生产者方法public synchronized void produce(int item) throws InterruptedException {while (queue.size() == CAPACITY) {wait(); // 等待队列有空间}queue.add(item);notifyAll(); // 通知消费者有新元素}// 消费者方法public synchronized int consume() throws InterruptedException {while (queue.isEmpty()) {wait(); // 等待队列有元素}int item = queue.poll();notifyAll(); // 通知生产者有空间return item;}
    }
    

四、优化建议

1. 缩小同步块范围
  • 反例
    public synchronized void process() {// 非关键代码long startTime = System.currentTimeMillis();// 关键代码(需要同步)synchronized (this) {// 操作共享资源}// 非关键代码System.out.println("耗时:" + (System.currentTimeMillis() - startTime));
    }
    
  • 正例:仅同步关键代码:
    public void process() {long startTime = System.currentTimeMillis();// 仅同步关键代码synchronized (this) {// 操作共享资源}System.out.println("耗时:" + (System.currentTimeMillis() - startTime));
    }
    
2. 优先使用同步代码块而非同步方法
  • 同步方法默认锁的是this,可能导致锁范围过大;
  • 同步代码块可精确控制锁对象。
3. 使用细粒度锁替代粗粒度锁
  • 反例
    public class Bank {private Map<String, Account> accounts = new HashMap<>();// 粗粒度锁:整个方法同步public synchronized void transfer(String from, String to, double amount) {// 转账逻辑}
    }
    
  • 正例
    public class Bank {private Map<String, Account> accounts = new HashMap<>();// 细粒度锁:仅锁相关账户public void transfer(String from, String to, double amount) {Account fromAccount = accounts.get(from);Account toAccount = accounts.get(to);// 按顺序加锁,避免死锁Account first = fromAccount.hashCode() < toAccount.hashCode() ? fromAccount : toAccount;Account second = first == fromAccount ? toAccount : fromAccount;synchronized (first) {synchronized (second) {// 转账逻辑}}}
    }
    
4. 考虑替代方案
  • 原子类(如AtomicInteger)替代简单计数器的synchronized
  • 读写锁ReentrantReadWriteLock)替代读多写少场景的synchronized
  • 并发容器(如ConcurrentHashMap)替代synchronized Map

五、总结

synchronized 关键字的核心特点:

  • 优点
    • 使用简单,无需手动释放锁;
    • 锁升级机制在不同场景下有较好的性能表现;
    • 支持线程间协作(wait/notify)。
  • 缺点
    • 无法中断正在等待锁的线程;
    • 不支持超时获取锁;
    • 锁粒度较粗(要么全锁,要么全放)。

适用场景

  • 简单的同步需求(如保护共享资源、实现原子操作);
  • 需配合wait/notify实现线程间协作。

在高并发场景下,若性能敏感,可考虑使用ReentrantLockStampedLock等更灵活的锁机制。

http://www.dtcms.com/a/267895.html

相关文章:

  • Flink ClickHouse 连接器数据读取源码深度解析
  • G-sensor运动检测功能开源:打破技术壁垒,加速智能硬件开发!
  • Java JDBC的初步了解
  • 力扣网编程45题:跳跃游戏II之正向查找方法(中等)
  • 【深度学习新浪潮】AI在材料力学领域的研究进展一览
  • 基于51单片机智能婴儿床
  • SQL 一键生成 Go Struct!支持字段注释、类型映射、结构体命名规范
  • 从前端转go开发的学习路线
  • 3、Configuring Topics
  • I-Cache、D-Cache 和 SRAM 的区别与联系
  • 系统架构设计师论文分享-论软件体系结构的演化
  • Docker容器中安装MongoDB,导入数据
  • nvm常用指令汇总
  • Spark流水线数据质量检查组件
  • 【认知】如何在高强度工作中保持心理健康和情绪稳定?
  • WizTree v4.2.5 x86 x64 单文件版
  • 让你的asp.net网站在调试模式下也能在局域网通过ip访问
  • Java 双亲委派机制笔记
  • GitCode项目创建指南
  • 一文掌握Qt Quick数字图像处理项目开发(基于Qt 6.9 C++和QML,代码开源)
  • 【黑马点评】(二)缓存
  • PyTorch 2.7深度技术解析:新一代深度学习框架的革命性演进
  • Python作业1
  • 实现Spring MVC登录验证与拦截器保护:从原理到实战
  • Jiraph​ 简介
  • React 各颜色转换方法、颜色值换算工具HEX、RGB/RGBA、HSL/HSLA、HSV、CMYK
  • AcWing--873.欧拉函数
  • ARMv8 创建1、2、3级页表代码与注释
  • 【C++基础】内存管理四重奏:malloc/free vs new/delete - 面试高频考点与真题解析
  • Windows 11 Enterprise LTSC 转 IoT