深入理解 synchronized:到底锁的是谁?

在 Java 多线程并发编程里,synchronized 是保证线程安全的核心关键字,但很多开发者只知道它能加锁,却不清楚它到底锁的是什么不同写法锁的范围有何区别

这篇文章就把 synchronized 的锁对象、作用范围、经典测试用例一次性讲透,代码与测试结果完全还原,帮你彻底搞懂 synchronized 的 "锁事"。


一、synchronized 锁的核心结论

synchronized 本质锁的是对象,而非代码!不同写法对应不同锁对象:

  1. 修饰实例方法 → 锁 当前实例对象(this)
  2. 修饰静态方法 → 锁 当前类的 Class 对象
  3. 修饰代码块 → 锁 括号内指定的对象

只有锁的是同一个对象,多线程才会互斥等待;锁不同对象,互不影响。


二、完整测试用例(一模一样还原)

测试用例 1:synchronized 修饰实例方法(锁 this)

代码

复制代码
public class SyncInstanceMethod {
    // 同步实例方法:锁当前对象 this
    public synchronized void test() {
        System.out.println(Thread.currentThread().getName() + " 进入同步方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 退出同步方法");
    }

    public static void main(String[] args) {
        // 同一个对象
        SyncInstanceMethod instance = new SyncInstanceMethod();

        new Thread(() -> instance.test(), "线程A").start();
        new Thread(() -> instance.test(), "线程B").start();
    }
}

执行结果

复制代码
线程A 进入同步方法
线程A 退出同步方法
线程B 进入同步方法
线程B 退出同步方法

结论

同一个实例对象的同步方法,多线程互斥执行,锁是当前实例 this。


测试用例 2:不同实例调用实例同步方法(互不锁)

代码

复制代码
public class SyncInstanceMethodDiff {
    public synchronized void test() {
        System.out.println(Thread.currentThread().getName() + " 进入同步方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 退出同步方法");
    }

    public static void main(String[] args) {
        // 两个不同实例
        SyncInstanceMethodDiff instance1 = new SyncInstanceMethodDiff();
        SyncInstanceMethodDiff instance2 = new SyncInstanceMethodDiff();

        new Thread(() -> instance1.test(), "线程A").start();
        new Thread(() -> instance2.test(), "线程B").start();
    }
}

执行结果

复制代码
线程A 进入同步方法
线程B 进入同步方法
线程A 退出同步方法
线程B 退出同步方法

结论

不同实例的同步方法,锁对象不同,不互斥,并发执行


测试用例 3:synchronized 修饰静态方法(锁 Class 对象)

代码

复制代码
public class SyncStaticMethod {
    // 同步静态方法:锁当前类 Class 对象
    public static synchronized void test() {
        System.out.println(Thread.currentThread().getName() + " 进入静态同步方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 退出静态同步方法");
    }

    public static void main(String[] args) {
        new Thread(() -> SyncStaticMethod.test(), "线程A").start();
        new Thread(() -> SyncStaticMethod.test(), "线程B").start();
    }
}

执行结果

复制代码
线程A 进入静态同步方法
线程A 退出静态同步方法
线程B 进入静态同步方法
线程B 退出静态同步方法

结论

静态同步方法锁的是类的 Class 对象,全局唯一,所有线程互斥。


测试用例 4:synchronized 修饰代码块(指定锁对象)

代码

复制代码
public class SyncBlock {
    // 自定义锁对象
    private final Object lock = new Object();

    public void test() {
        // 同步代码块:锁 lock 对象
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " 进入同步代码块");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 退出同步代码块");
        }
    }

    public static void main(String[] args) {
        SyncBlock instance = new SyncBlock();
        new Thread(() -> instance.test(), "线程A").start();
        new Thread(() -> instance.test(), "线程B").start();
    }
}

执行结果

复制代码
线程A 进入同步代码块
线程A 退出同步代码块
线程B 进入同步代码块
线程B 退出同步代码块

结论

同步代码块锁指定对象,粒度更灵活,是企业开发推荐写法。


测试用例 5:实例锁 VS 类锁(互不干扰)

代码

复制代码
public class SyncInstanceAndClass {
    // 实例方法锁:this
    public synchronized void instanceMethod() {
        System.out.println(Thread.currentThread().getName() + " 进入实例方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 退出实例方法");
    }

    // 静态方法锁:Class
    public static synchronized void staticMethod() {
        System.out.println(Thread.currentThread().getName() + " 进入静态方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 退出静态方法");
    }

    public static void main(String[] args) {
        SyncInstanceAndClass instance = new SyncInstanceAndClass();
        new Thread(() -> instance.instanceMethod(), "线程A").start();
        new Thread(() -> SyncInstanceAndClass.staticMethod(), "线程B").start();
    }
}

执行结果

复制代码
线程A 进入实例方法
线程B 进入静态方法
线程A 退出实例方法
线程B 退出静态方法

结论

实例锁(this)和类锁(Class)是两把完全独立的锁,互不阻塞。


三、一张表总结 synchronized 锁对象

写法 锁对象 作用范围
实例方法 synchronized 当前实例 this 同一实例互斥
静态方法 synchronized 当前类 Class 全局所有实例互斥
同步代码块 synchronized (obj) 指定对象 obj 仅锁 obj 范围

四、核心要点回顾

  1. synchronized 永远锁对象,不锁代码。
  2. 只有锁同一个对象,才会互斥。
  3. 实例锁和类锁是两把独立锁,互不影响。
  4. 代码块锁粒度最细,性能最优,优先使用。

搞懂 synchronized 到底锁谁,是写出高并发、线程安全代码的第一步!

相关推荐
艾莉丝努力练剑4 小时前
【Linux网络】Linux 网络编程:传输层TCP(二)
linux·运维·服务器·网络·tcp/ip·计算机网络
都在酒里4 小时前
Linux字符设备驱动开发(九):内核定时器——实现LED周期性闪烁与轮询驱动原理
linux·运维·驱动开发·交互
都在酒里5 小时前
Linux字符设备驱动开发(十):综合实例——I2C传感器 + LED智能控制与进阶指南
linux·运维·服务器·驱动开发·交互
wanhengidc12 小时前
服务器租用有何优点
运维·服务器·安全·web安全
ZGi.ai13 小时前
人工审查节点:让自动化工作流多一步人工把关
运维·人工智能·自动化·人机协同·智能体工作流·人工审查
坤昱13 小时前
cfs调度类深入解刨——最新内核细节分析2
linux·服务器·cfs·cfs调度·eevdf调度·eevdf·kernel 7.1
艾莉丝努力练剑13 小时前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext
海市公约13 小时前
Linux核心基础命令与权限管理实战指南
linux·运维·服务器·vim·权限管理·系统监控·命令行
wkd_00713 小时前
Ubuntu 22.04 Samba 连接故障排查记:从“用户名或密码错误”到 NTLM 版本不兼容
linux·运维·ubuntu
企服AI产品测评局14 小时前
Agent适配信创环境实测:企业级自动化如何实现国产操作系统与数据库全兼容?
运维·数据库·人工智能·ai·chatgpt·自动化