当前位置: 代码网 > it编程>编程语言>Java > Java synchronized 关键字原理、用法、优化与实战指南

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

2026年04月18日 Java 我要评论
一、synchronized 核心定义synchronized(同步的)是 java 的关键字,用于实现线程互斥和同步,核心作用:原子性:保证临界区代码(被 synchronized 修饰的代码)同一

一、synchronized 核心定义

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

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

设计初衷:以最简单的方式解决多线程并发访问共享资源的安全问题,无需手动管理锁的生命周期(jvm 自动加锁 / 释放锁)。

二、synchronized 解决的核心问题

多线程下对共享资源的 “复合操作”(如 i++、修改集合、更新对象属性)会导致线程安全问题(如数据脏读、重复写入、值覆盖),synchronized 通过 “互斥锁” 解决该问题:

  • 互斥性:同一时刻只有一个线程能进入同步代码块 / 方法;
  • 排他性:持有锁的线程可重复进入(可重入锁),其他线程需等待锁释放。

线程安全问题演示(无 synchronized)

public class syncproblem {
    // 共享资源
    private int count = 0;
    // 非同步方法:多线程下count++会出现值覆盖
    public void increment() {
        count++; // 复合操作:读→加1→写,非原子性
    }
    public static void main(string[] args) throws interruptedexception {
        syncproblem demo = new syncproblem();
        // 10个线程,每个执行1000次increment
        for (int i = 0; i < 10; i++) {
            new thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            }).start();
        }
        thread.sleep(2000);
        // 预期10000,实际远小于(如9876)
        system.out.println("count最终值:" + demo.count);
    }
}

三、synchronized 核心用法

synchronized 可修饰实例方法、静态方法、代码块,不同修饰方式对应不同的 “锁对象”:

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

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

修复上面的线程安全问题

public class syncinstancemethod {
    private int count = 0;
    // synchronized修饰实例方法:锁对象是this
    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);
        // 预期10000,实际准确
        system.out.println("count最终值:" + demo.count);
    }
}

3.2 修饰静态方法(类锁)

  • 锁对象:当前类的 class 对象(如 syncstaticmethod.class);
  • 特点:所有实例的该静态方法互斥(因为类对象唯一)。

示例

public class syncstaticmethod {
    private static int staticcount = 0;
    // synchronized修饰静态方法:锁对象是syncstaticmethod.class
    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);
        // 预期2000,实际准确
        system.out.println("staticcount最终值:" + syncstaticmethod.staticcount);
    }
}

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

  • 锁对象:手动指定(如 this、类对象、任意非 null 对象);
  • 特点:粒度更细,仅锁定关键代码,性能更高(推荐优先使用)。

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

public class synccodeblockthis {
    private int count = 0;
    public void increment() {
        // 仅锁定count++,而非整个方法
        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)是不同的锁对象,二者执行时互不阻塞。

完整示例代码

public class syncstaticvsinstance {
    // 计数器:记录方法执行顺序
    private static int step = 0;
    // 1. synchronized修饰实例方法(锁对象:this)
    public synchronized void instancesyncmethod() {
        system.out.println(thread.currentthread().getname() + " 进入实例同步方法");
        try {
            // 模拟耗时操作(3秒),放大执行差异
            thread.sleep(3000);
            step++;
            system.out.println(thread.currentthread().getname() + " 实例同步方法执行完成,step=" + step);
        } catch (interruptedexception e) {
            e.printstacktrace();
        }
    }
    // 2. static synchronized修饰静态方法(锁对象:syncstaticvsinstance.class)
    public static synchronized void staticsyncmethod() {
        system.out.println(thread.currentthread().getname() + " 进入静态同步方法");
        try {
            // 模拟耗时操作(3秒)
            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();
        // 线程1:调用实例同步方法(锁this)
        thread thread1 = new thread(() -> demo.instancesyncmethod(), "线程1");
        // 线程2:调用静态同步方法(锁类对象)
        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,说明两个方法都完成了耗时操作,进一步验证无阻塞。

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

若希望实例方法和静态方法互斥,需将二者的锁对象统一为类对象,修改实例方法为 “锁类对象的同步代码块”:

public class syncunifiedlock {
    private static int step = 0;
    // 改造实例方法:锁类对象(而非this),与静态方法互斥
    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();
            }
        }
    }
    // 静态同步方法(锁对象:syncunifiedlock.class)
    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

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

4.1 字节码层面

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

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

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

java 6 后,jvm 对 synchronized 做了大量优化,使其性能大幅提升,核心优化点:

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

锁升级流程:偏向锁 → 轻量级锁 → 重量级锁(不可逆)。

五、synchronized 在 jdk 源码中的应用

5.1 集合类(如 vector、hashtable)

vector 的 add()get() 等方法被 synchronized 修饰,保证线程安全(但性能低,推荐用 copyonwritearraylist):

public class vector<e> extends abstractlist<e> {
    // synchronized修饰实例方法:保证线程安全
    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 ()

synchronized 是使用 wait()notify()notifyall() 的前提(必须在同步代码块 / 方法中调用):

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 java.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 锁支持可中断获取。

七、synchronized vs lock(reentrantlock)

特性synchronizedreentrantlock(lock 接口实现)
锁实现jvm 层面(内置锁)jdk 层面(手动实现)
可重入性支持支持(可指定公平 / 非公平)
中断性不支持(阻塞不可中断)支持(lockinterruptibly ())
超时获取不支持支持(trylock (long time))
公平锁仅非公平锁可指定公平 / 非公平锁
条件变量仅 wait ()/notify ()(单一条件)支持多个 condition(精准唤醒)
性能java 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) {
        // 线程1:持有lock1,等待lock2
        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();
        // 线程2:持有lock2,等待lock1
        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);
  • 使用 locktrylock() 超时机制;
  • 避免嵌套锁。

总结

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

synchronized 是 java 并发编程的基础,掌握它的原理和用法,是写出安全、高效并发代码的关键。日常开发中,优先使用 synchronized(简单、不易出错),复杂场景(如公平锁、可中断、多条件唤醒)再考虑 lock。

到此这篇关于java synchronized 关键字原理、用法、优化与实战指南的文章就介绍到这了,更多相关java synchronized 关键字用法内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com