展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > Javasynchronized关键字原理、用法、优化与实战指南

Javasynchronized关键字原理、用法、优化与实战指南

synchronized是Java关键字,通过互斥锁实现原子性、可见性和有序性,解决多线程复合操作的安全问题。用法包括修饰实例方法(对象锁)、静态方法(类锁)和代码块(自定义锁对象),其中类锁与实例锁互不干扰。底层通过monitorenter和monitorexit指令实现,JVM对锁进行优化。

本文目录

    一、synchronized 核心定义

    synchronized(同步的)是 Ja va 的关键字,用于实现线程互斥和同步,核心作用:

    Ja vasynchronized关键字原理、用法、优化与实战指南

    1. 原子性:保证临界区代码(被 synchronized 修饰的代码)同一时间只有一个线程执行,解决复合操作的线程安全问题;
    2. 可见性:线程释放锁时,会将工作内存中的修改刷新到主内存;获取锁时,会清空工作内存,从主内存重新加载变量,保证变量值的实时同步;
    3. 有序性:通过 “锁的获取 - 释放” 规则,阻止指令重排序(临界区代码按顺序执行)。

    设计初衷其实很朴素——用最简单的方式解决多线程并发访问共享资源的安全问题,不需要开发者手动管理锁的生命周期,JVM 自动完成加锁和释放。

    二、synchronized 解决的核心问题

    多线程下对共享资源的“复合操作”(比如 i++、修改集合、更新对象属性)很容易引发线程安全问题,比如数据脏读、重复写入、值覆盖。而 synchronized 通过“互斥锁”机制,从根本上解决了这一隐患:

    • 互斥性:同一时刻只有一个线程能进入同步代码块或方法;
    • 排他性:持有锁的线程可以重复进入(可重入锁),其他线程只能老老实实在外面等着锁释放。

    先来看一个没有 synchronized 保护的例子,感受一下问题有多严重:

    public class SyncProblem {
        private int count = 0;
        public void increment() {
            count++; // 复合操作:读→加1→写,非原子性
        }
        public static void main(String[] args) throws InterruptedException {
            SyncProblem demo = new SyncProblem();
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 1000; j++) {
                        demo.increment();
                    }
                }).start();
            }
            Thread.sleep(2000);
            System.out.println("count最终值:" + demo.count);
        }
    }

    这段代码开10个线程,每个线程执行1000次自增,预期结果是10000。但由于 count++ 不是原子操作,频繁的线程切换会导致值覆盖,最终结果往往远小于10000。这就是并发编程里最常见的坑。

    三、synchronized 核心用法

    synchronized 可以修饰实例方法、静态方法、代码块,不同的修饰方式对应不同的“锁对象”,理解这一点至关重要。

    3.1 修饰实例方法(对象锁)

    • 锁对象:当前实例(this);
    • 特点:不同实例的同步方法互不影响,同一实例的同步方法互斥。

    用 synchronized 修复上面的问题,只需在 increment() 方法前加上 synchronized 即可:

    public class SyncInstanceMethod {
        private int count = 0;
        public synchronized void increment() {
            count++;
        }
        public static void main(String[] args) throws InterruptedException {
            SyncInstanceMethod demo = new SyncInstanceMethod();
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 1000; j++) {
                        demo.increment();
                    }
                }).start();
            }
            Thread.sleep(2000);
            System.out.println("count最终值:" + demo.count);
        }
    }

    这次结果稳稳地停在10000,因为每次 count++ 都被锁保护,同一时间只有一个线程能执行。

    3.2 修饰静态方法(类锁)

    • 锁对象:当前类的 Class 对象(比如 SyncStaticMethod.class);
    • 特点:所有实例的该静态方法互斥(因为类对象在 JVM 中只有一份)。

    看个例子:

    public class SyncStaticMethod {
        private static int staticCount = 0;
        public static synchronized void staticIncrement() {
            staticCount++;
        }
        public static void main(String[] args) throws InterruptedException {
            SyncStaticMethod demo1 = new SyncStaticMethod();
            SyncStaticMethod demo2 = new SyncStaticMethod();
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo1.staticIncrement();
                }
            }).start();
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo2.staticIncrement();
                }
            }).start();
            Thread.sleep(2000);
            System.out.println("staticCount最终值:" + SyncStaticMethod.staticCount);
        }
    }

    这里 demo1 和 demo2 虽然是两个不同的实例,但 staticIncrement() 是静态同步方法,锁的是类对象本身,所以两个线程仍然互斥执行,结果正确。

    3.3 修饰代码块(自定义锁对象)

    • 锁对象:手动指定,可以是 this、类对象或者任意非 null 对象;
    • 特点:粒度更细,只锁定关键代码,性能更好(推荐优先使用)。

    示例 1:锁当前实例(this)

    public class SyncCodeBlockThis {
        private int count = 0;
        public void increment() {
            synchronized (this) {
                count++;
            }
        }
        public void printCount() {
            System.out.println("count:" + count);
        }
    }

    示例 2:锁类对象(类锁)

    public class SyncCodeBlockClass {
        private static int staticCount = 0;
        public void staticIncrement() {
            synchronized (SyncCodeBlockClass.class) {
                staticCount++;
            }
        }
    }

    示例 3:锁自定义对象(私有锁,推荐)

    public class SyncCodeBlockCustom {
        private int count = 0;
        private final Object lock = new Object();
        public void increment() {
            synchronized (lock) {
                count++;
            }
        }
    }

    使用私有锁对象可以避免外部对象干扰,是工程实践中比较推荐的做法。

    3.4 关键对比:static synchronized 与 synchronized 方法的执行逻辑

    核心结论

    static synchronized(类锁,锁对象为类的 Class 对象)和 synchronized(实例锁,锁对象为 this)是不同的锁对象,二者执行时互不阻塞。这一点常常被初学者忽略,甚至导致一些“你以为互斥但实际没互斥”的 bug。

    完整示例代码

    public class SyncStaticVsInstance {
        private static int step = 0;
    
        public synchronized void instanceSyncMethod() {
            System.out.println(Thread.currentThread().getName() + " 进入实例同步方法");
            try {
                Thread.sleep(3000);
                step++;
                System.out.println(Thread.currentThread().getName() + " 实例同步方法执行完成,step=" + step);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static synchronized void staticSyncMethod() {
            System.out.println(Thread.currentThread().getName() + " 进入静态同步方法");
            try {
                Thread.sleep(3000);
                step++;
                System.out.println(Thread.currentThread().getName() + " 静态同步方法执行完成,step=" + step);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            SyncStaticVsInstance demo = new SyncStaticVsInstance();
            Thread thread1 = new Thread(() -> demo.instanceSyncMethod(), "线程1");
            Thread thread2 = new Thread(() -> SyncStaticVsInstance.staticSyncMethod(), "线程2");
            thread1.start();
            thread2.start();
        }
    }

    执行结果(无阻塞,并行执行)

    线程1 进入实例同步方法

    线程2 进入静态同步方法

    // 等待3秒后,两个方法同时完成

    线程1 实例同步方法执行完成,step=1

    线程2 静态同步方法执行完成,step=2

    结果分析

    1. 无阻塞:线程1和线程2几乎同时进入各自同步方法,没有先后等待——因为实例锁和类锁是两个完全独立的锁对象;
    2. 执行时序:两个方法各耗时3秒,但总执行时间仅约3秒(而非6秒),证明二者并行执行;
    3. step 计数:step 最终为2,说明两个方法都完成了耗时操作,进一步验证无阻塞。

    扩展:如何让两个方法互斥?(统一锁对象)

    如果实际需求是希望实例方法和静态方法互斥执行,那就需要把二者的锁对象统一为类对象。改造实例方法,让它也用 synchronized (SyncUnifiedLock.class) 包裹:

    public class SyncUnifiedLock {
        private static int step = 0;
    
        public void instanceSyncMethod() {
            synchronized (SyncUnifiedLock.class) {
                System.out.println(Thread.currentThread().getName() + " 进入实例同步方法");
                try {
                    Thread.sleep(3000);
                    step++;
                    System.out.println(Thread.currentThread().getName() + " 实例同步方法执行完成,step=" + step);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static synchronized void staticSyncMethod() {
            System.out.println(Thread.currentThread().getName() + " 进入静态同步方法");
            try {
                Thread.sleep(3000);
                step++;
                System.out.println(Thread.currentThread().getName() + " 静态同步方法执行完成,step=" + step);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            SyncUnifiedLock demo = new SyncUnifiedLock();
            Thread thread1 = new Thread(() -> demo.instanceSyncMethod(), "线程1");
            Thread thread2 = new Thread(() -> SyncUnifiedLock.staticSyncMethod(), "线程2");
            thread1.start();
            thread2.start();
        }
    }

    改造后执行结果(互斥执行)

    线程1 进入实例同步方法

    // 等待3秒后线程1完成

    线程1 实例同步方法执行完成,step=1

    // 线程2才开始执行

    线程2 进入静态同步方法

    // 再等待3秒后线程2完成

    线程2 静态同步方法执行完成,step=2

    可以看到,总执行时间变成了6秒,两个线程串行执行,这才是真正的互斥。

    四、synchronized 底层原理(JVM 层面)

    4.1 字节码层面

    synchronized 修饰的代码块或方法,编译成字节码后会生成 monitorenter(进入锁)和 monitorexit(释放锁)指令:

    • monitorenter:尝试获取 monitor 对象的所有权,成功就进入,失败则阻塞;
    • monitorexit:释放锁,唤醒等待队列中的线程。

    4.2 JVM 锁优化(从重量级到轻量级)

    Ja va 6 之后,JVM 对 synchronized 做了大量优化,性能大幅提升,甚至在某些场景下已经不输 ReentrantLock。核心优化点包括:

    1. 偏向锁:针对单线程重复获取同一锁的场景,减少锁竞争开销(默认开启);
    2. 轻量级锁:多线程交替获取锁(无竞争),通过 CAS 实现,不需要操作系统内核态切换;
    3. 重量级锁:多线程竞争激烈,需要操作系统介入(阻塞线程),性能较低;
    4. 锁消除:JVM 识别到无共享资源的同步代码,自动移除锁;
    5. 锁粗化:将多次连续的锁获取/释放合并为一次,减少开销。

    锁升级的路径是单向的:偏向锁 → 轻量级锁 → 重量级锁(不可逆)。

    五、synchronized 在 JDK 源码中的应用

    5.1 集合类(如 Vector、Hashtable)

    像 Vector 的 add()、get() 等方法都加了 synchronized 修饰,保证线程安全。不过这种全方法同步的方式性能较低,现在更推荐用 CopyOnWriteArrayList 等更高效的替代方案:

    public class Vector extends AbstractList {
        public synchronized boolean add(E e) {
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
        }
        public synchronized E get(int index) {
            if (index >= elementCount)
                throw new ArrayIndexOutOfBoundsException(index);
            return elementData(index);
        }
    }

    5.2 Object 类的 wait ()/notify ()

    wait()、notify()、notifyAll() 必须在 synchronized 代码块或方法中调用,否则会抛出 IllegalMonitorStateException:

    public class WaitNotifyDemo {
        private final Object lock = new Object();
        public void waitDemo() throws InterruptedException {
            synchronized (lock) {
                lock.wait();
            }
        }
        public void notifyDemo() {
            synchronized (lock) {
                lock.notify();
            }
        }
    }

    5.3 ja va.lang.Thread 的 join () 方法

    join() 底层也是通过 synchronized + wait() 实现的:

    public class Thread implements Runnable {
        public final synchronized void join(long millis) throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                // ...
            }
        }
    }

    六、synchronized 核心特性

    6.1 可重入性

    synchronized 是可重入锁:同一线程持有锁后,可以再次进入该锁的同步代码,避免了自己把自己锁死的尴尬。

    看个简单例子:

    public class ReentrantSync {
        public synchronized void method1() {
            System.out.println("进入method1");
            method2();
        }
        public synchronized void method2() {
            System.out.println("进入method2");
        }
        public static void main(String[] args) {
            ReentrantSync demo = new ReentrantSync();
            demo.method1();
            // 输出:
            // 进入method1
            // 进入method2
        }
    }

    6.2 不可中断性

    线程获取锁失败时,会进入阻塞状态,而且无法被中断(必须等到获取到锁才能继续)。相比之下,Lock 接口提供了 lockInterruptibly() 方法,支持可中断获取锁。

    七、synchronized vs Lock(ReentrantLock)

    特性synchronizedReentrantLock(Lock 接口实现)
    锁实现JVM 层面(内置锁)JDK 层面(手动实现)
    可重入性支持支持(可指定公平/非公平)
    中断性不支持(阻塞不可中断)支持(lockInterruptibly())
    超时获取不支持支持(tryLock(long time))
    公平锁仅非公平锁可指定公平/非公平锁
    条件变量仅 wait()/notify()(单一条件)支持多个 Condition(精准唤醒)
    性能Ja va 6 后优化,与 Lock 接近高并发下略优
    使用复杂度简单(自动加锁/释放)复杂(需手动 unlock(),建议 try-finally)

    八、synchronized 常见面试题 & 易错点

    8.1 面试高频问题

    1. synchronized 能保证可见性和有序性吗?能。释放锁时会刷新主内存(可见性),锁的获取/释放会阻止指令重排序(有序性),当然原子性也是基本盘。
    2. synchronized 是公平锁吗?不是。它默认是非公平锁,而且无法指定为公平锁。JVM 并不会保证先等待的线程一定先获取锁。
    3. synchronized 修饰实例方法和静态方法的锁对象有什么区别?
      • 实例方法:锁是 this(当前实例);
      • 静态方法:锁是 类名.class(类对象);两者互不干扰(锁对象不同)。
    4. 为什么 synchronized 是可重入的?JVM 会记录锁的持有线程和重入次数,线程再次获取同一锁时仅增加重入次数,释放时减少次数,次数为0时才真正释放锁。
    5. 一个类中同时有 static synchronized 和 synchronized 方法,多线程调用会互斥吗?不会。因为锁对象不同(类对象 vs this),除非你统一了锁对象。

    8.2 开发易错点

    1. 锁对象为 null:synchronized 的锁对象不能为 null,编译能通过,但运行时会抛 NullPointerException;
    2. 锁粒度太大:把整个方法都同步,而不是只同步关键代码,导致性能低下;
    3. 混淆对象锁和类锁:以为静态同步方法和实例同步方法互斥,实际上它们互不干扰,除非特意统一锁对象;
    4. 死锁:多个线程互相持有对方需要的锁,比如线程 A 持有锁1等待锁2,线程 B 持有锁2等待锁1。

    死锁的经典示例:

    public class DeadLockDemo {
        private static final Object lock1 = new Object();
        private static final Object lock2 = new Object();
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (lock1) {
                    System.out.println("线程1持有lock1,等待lock2");
                    try { Thread.sleep(100); } catch (InterruptedException e) {}
                    synchronized (lock2) {
                        System.out.println("线程1获取lock2");
                    }
                }
            }).start();
            new Thread(() -> {
                synchronized (lock2) {
                    System.out.println("线程2持有lock2,等待lock1");
                    try { Thread.sleep(100); } catch (InterruptedException e) {}
                    synchronized (lock1) {
                        System.out.println("线程2获取lock1");
                    }
                }
            }).start();
        }
    }

    这个程序跑起来后两个线程会永远阻塞,因为谁都不肯放手。

    死锁解决方案:

    • 固定锁的获取顺序(比如所有线程先获取 lock1 再获取 lock2);
    • 使用 Lock 的 tryLock() 超时机制;
    • 尽量避免嵌套锁。

    总结

    1. synchronized 核心作用就是保证原子性、可见性、有序性,通过互斥锁实现多线程安全;
    2. 三种用法:修饰实例方法(锁 this)、静态方法(锁类对象)、代码块(自定义锁对象,最推荐);
    3. static synchronized(类锁)和 synchronized(实例锁)锁对象不同,默认互不阻塞,需要统一锁对象才能实现互斥;
    4. Ja va 6 以后 JVM 做了偏向锁、轻量级锁等优化,性能大幅提升,不用担心“重量级”问题;
    5. 核心边界:可重入、非公平、不可中断,锁对象不能为 null,避免死锁和过大的锁粒度。

    synchronized 是 Ja va 并发编程的基础,掌握它的原理和用法,是写出安全、高效并发代码的关键。日常开发中建议优先使用 synchronized——简单、不易出错,只有在需要公平锁、可中断、多条件唤醒等复杂场景时,才考虑 Lock。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirServer Windows版
    AirServer Windows版

    AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。