啥是synchronized

synchronized 的 3 种用法详解(附代码实战)​

以下是 Java 中 synchronized 的三种核心用法及详细示例,结合底层原理和实际场景解释其作用机制。


1. 修饰实例方法:锁住当前对象

代码示例

java 复制代码
public class Counter {
    private int count = 0;
    // 实例方法加锁
    public synchronized void add() {
        count++;
    }
}

解释

  • 锁对象 :当前实例(this)。

  • 作用范围 :同一实例的多个线程会竞争锁,不同实例的线程互不影响。例如,Counter c1 = new Counter();Counter c2 = new Counter();add() 方法可被不同线程同时执行。

  • 适用场景:实例变量需要线程安全操作的场景,如计数器、订单处理等。

底层原理

JVM 通过对象头中的 Mark Word 标记锁状态。当一个线程进入 add() 方法时,会获取当前对象的 Monitor 锁,其他线程必须等待锁释放。


2. 修饰静态方法:锁住类对象

代码示例

java 复制代码
public class GlobalCounter {
    private static int count = 0;
    // 静态方法加锁
    public static synchronized void add() {
        count++;
    }
}

解释

  • 锁对象 :类的 Class 对象(如 GlobalCounter.class)。

  • 作用范围 :所有实例共享同一把锁。例如,GlobalCounter.add() 被线程 A 执行时,线程 B 无论通过哪个实例调用 add() 都会被阻塞。

  • 适用场景:静态变量或全局资源的同步操作,如全局配置管理、单例模式等。

底层原理

静态方法的锁对应类的元数据(Class 对象),Monitor 机制会阻止所有实例的并发访问。


3. 修饰代码块:灵活控制锁粒度

代码示例

java 复制代码
public class TransactionService {
    private final Object lock = new Object(); // 自定义锁对象
    private List<String> logs = new ArrayList<>();

    public void logTransaction(String message) {
        // 非同步操作(其他线程可并行执行)
        System.out.println("Preparing log...");

        // 同步代码块(锁定自定义对象)
        synchronized (lock) {
            logs.add(message);
        }
    }
}

解释

  • 锁对象 :可以是任意对象(this、类对象、自定义对象等)。

  • 作用范围

    • ​**synchronized(this)**:锁当前实例,效果等同于修饰实例方法。

    • ​**synchronized(ClassName.class)**:锁类对象,效果等同于修饰静态方法。

    • 自定义对象锁:更细粒度控制,如只锁日志列表而非整个方法。

  • 适用场景:需要减少锁粒度以提升性能的场景,如仅保护共享资源的部分操作。

底层原理

代码块锁通过 monitorentermonitorexit 指令实现,编译器会在代码块前后插入这两个指令,确保锁的获取和释放。


4. 锁对象的选择与优化

关键原则

  • 避免锁字符串或基本类型 :因常量池缓存可能导致意外竞争(如 synchronized("LOCK") 可能全局锁死)。

  • 使用私有锁对象 :如 private final Object lock = new Object();,防止外部代码意外获取锁。

  • 减少锁范围:尽量缩小同步代码块,避免在锁内执行 I/O 或复杂计算。

示例对比

java 复制代码
// 错误示例:锁字符串(风险高)
synchronized ("GLOBAL_LOCK") { ... }

// 正确示例:锁私有对象(安全)
private final Object lock = new Object();
synchronized (lock) { ... }

5. 进阶:锁升级与性能优化

JDK 1.6 后引入锁升级机制,减少锁竞争开销:

  1. 偏向锁:记录第一个获取锁的线程 ID,无竞争时直接进入。

  2. 轻量级锁:通过 CAS 自旋尝试获取锁,适用于低竞争场景。

  3. 重量级锁:竞争激烈时升级为操作系统级互斥锁,线程进入阻塞队列。

代码实战

java 复制代码
// 高竞争场景(可能触发重量级锁)
public class HighContentionDemo {
    private static int counter = 0;
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                synchronized (HighContentionDemo.class) {
                    counter++;
                }
            }).start();
        }
    }
}

此时 JVM 可能直接将锁升级为重量级锁,避免自旋带来的 CPU 浪费。


总结

synchronized 的三种用法对应不同粒度的线程安全控制:

  1. 实例方法锁:保护实例变量,适用于对象级别的并发控制。

  2. 静态方法锁:保护静态变量,全局唯一锁。

  3. 代码块锁:灵活控制锁对象,优化性能的关键手段。

合理选择锁对象和范围,结合 JVM 的锁升级机制,能在保证线程安全的同时最大限度提升性能。

相关推荐
付朝鲜2 分钟前
用自写的jQuery库+Ajax实现了省市联动
java·前端·javascript·ajax·jquery
小赵面校招4 分钟前
Spring Boot整合MyBatis全攻略:原理剖析与最佳实践
java·spring boot·mybatis
曼岛_7 分钟前
[Java实战]Spring Boot 3 整合 Ehcache 3(十九)
java·spring boot·spring
意倾城9 分钟前
Spring Boot 配置文件敏感信息加密:Jasypt 实战
java·spring boot·后端
曼岛_9 分钟前
[Java实战]Spring Boot 3 整合 Apache Shiro(二十一)
java·spring boot·apache
火皇4059 分钟前
Spring Boot 使用 OSHI 实现系统运行状态监控接口
java·spring boot·后端
带刺的坐椅12 分钟前
Java Solon v3.3.0 发布(国产优秀应用开发基座)
java·spring·solon
不再幻想,脚踏实地13 分钟前
Spring Boot配置文件
java·数据库·spring boot
pedestrian_h20 分钟前
Spring AI 开发本地deepseek对话快速上手笔记
java·spring boot·笔记·llm·ollama·deepseek
诚丞成21 分钟前
BFS算法篇——从晨曦到星辰,BFS算法在多源最短路径问题中的诗意航行(上)
java·算法·宽度优先