啥是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 的锁升级机制,能在保证线程安全的同时最大限度提升性能。

相关推荐
IDOlaoluo1 小时前
Linux 安装 JDK 8u291 教程(jdk-8u291-linux-x64.tar.gz 解压配置详细步骤)
java·linux·运维
都叫我大帅哥1 小时前
深入解剖Shenandoah GC:Java低延迟垃圾回收的终极武器
java·jvm
带只拖鞋去流浪1 小时前
Java文件读写(IO、NIO)
java·开发语言·nio
戴誉杰2 小时前
JAVA 程序员cursor 和idea 结合编程
java·ide·intellij-idea·cursor
阿狗哲哲2 小时前
Java选手如何看待Golang
java·开发语言·golang
苹果醋35 小时前
React Native jpush-react-native极光推送 iOS生产环境接收不到推送
java·运维·spring boot·mysql·nginx
老华带你飞5 小时前
数码论坛|基于SprinBoot+vue的数码论坛系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·数码论坛系统
葵野寺5 小时前
【JVM】深入解析Java虚拟机
java·linux·jvm·gc·垃圾回收
程序猿七度6 小时前
【FastExcel】解决ReadSheet在Map中获取对象不准确问题(已提交PR并合并到开源社区)
java·开源·fastexcel
AI风老师7 小时前
5、docker镜像管理命令
java·docker·eureka