深入理解 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 到底锁谁,是写出高并发、线程安全代码的第一步!

相关推荐
zzzzzz31013 小时前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode13 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220701 天前
如何搭建本地yum源(上)
运维
大树884 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质4 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz4 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工4 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智4 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_4 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化