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

相关推荐
RisunJan2 小时前
Linux命令-mkbootdisk(可建立目前系统的启动盘)
linux·运维·服务器
Sst的头号粉丝2 小时前
Docker——compose
运维·docker·容器
朽棘不雕2 小时前
Linux工具(上)
linux·运维·服务器
daad7773 小时前
bitcoin HD钱包示例 真实使命7
运维·服务器
Zero-Talent3 小时前
TCP/IP协议
运维·服务器·网络
桌面运维家3 小时前
Windows/Linux云桌面:高校VDisk方案部署指南
linux·运维·windows
Du_chong_huan3 小时前
1.7 计算机网络和因特网的历史 | 《计算机网络:自顶向下方法》精读版
运维·服务器·网络
ZZZKKKRTSAE3 小时前
rhel9快速上手Docker
运维·docker·容器
筱顾大牛3 小时前
Docker安装教程(加汉化!超详细!!!)
运维·docker·容器