synchronized是Java中用于解决并发问题的核心关键字它通过确保多个线程对共享资源的互斥访问来避免线程安全问题如竞态条件、数据不一致等。synchronized的核心特性原子性Atomicity确保一个或多个操作要么全部执行成功要么全部执行失败。在synchronized代码块中的代码是不可中断的同一时刻只有一个线程能执行。可见性Visibility保证一个线程对共享变量的修改对于其他后续进入同步代码块的线程是可见的。当线程释放锁时会将私有内存中的变量值刷新回主内存。有序性Ordering虽然编译器和处理器为了优化会进行指令重排但synchronized可以保证多线程程序在逻辑上的执行顺序即“同步块内的代码在执行上具有先后顺序”。可重入性可重入性是指一个线程已经获取到某个锁后再次请求该锁时可以直接获取无需重新竞争。synchronized是可重入锁其内部通过计数器记录锁的持有次数初始为0获取锁时加1释放锁时减1计数器为0时锁才被真正释放。这一特性避免了线程在递归调用同步方法/代码块时出现死锁。例如一个同步方法A调用另一个同步方法B两者锁对象相同线程获取A的锁后调用B时可直接获取锁。synchronized的使用方式synchronized的使用灵活可修饰不同的代码结构核心是明确“锁对象”——线程竞争的是锁对象只有获取到锁对象的线程才能执行同步代码常见使用方式有3种。修饰实例方法对象锁语法public synchronized void methodName() { ... }锁对象当前类的实例对象this。特点不同实例对象的锁相互独立即多个线程访问同一个实例的同步实例方法时会竞争锁访问不同实例的同步实例方法时因锁对象不同不会竞争。示例同一User实例的add()方法被多线程调用时互斥不同User实例的add()方法可并行执行。修饰静态方法类锁语法public static synchronized void methodName() { ... }锁对象当前类的Class对象每个类在JVM中只有一个Class对象是全局唯一的。特点类锁是全局锁无论创建多少个类的实例所有线程访问该类的同步静态方法时都会竞争同一个Class对象锁。注意类锁与对象锁相互独立即同步静态方法和同步实例方法的锁对象不同线程访问时不会竞争。修饰代码块自定义锁对象语法synchronized (锁对象) { ... 同步代码 ... }锁对象可自定义支持两种类型① 实例对象this或其他实例② Class对象类名.class。特点粒度最细可精准控制需要同步的代码片段而非整个方法减少锁竞争提高程序性能。常见场景锁当前实例synchronized (this) { ... }效果与修饰实例方法一致但仅同步代码块内的逻辑。锁Class对象synchronized (User.class) { ... }效果与修饰静态方法一致。锁自定义对象private Object lock new Object(); synchronized (lock) { ... }通过独立的锁对象避免与其他同步逻辑竞争锁灵活性最高。synchronized的锁机制Java 6及以后对synchronized进行了大幅优化引入了“偏向锁、轻量级锁、重量级锁”三种锁状态目的是根据线程竞争的激烈程度动态切换锁状态平衡性能与线程安全。锁机制的核心是“对象头”——Java对象在内存中的布局包括对象头、实例数据、对齐填充其中对象头存储了锁的状态信息Mark Word、类元数据指针等。三种锁状态的优先级无锁 偏向锁 轻量级锁 重量级锁随着线程竞争的加剧锁会从低级别向高级别升级且升级过程不可逆一旦升级为重量级锁无法回退为轻量级锁或偏向锁。无锁No Lock初始状态。偏向锁Biased Lock当只有一个线程访问同步块时直接在对象头记录线程ID下次该线程进入时无需CAS操作性能极高。轻量级锁Lightweight Lock当出现竞争但竞争不激烈时通过CAS自旋来尝试获取锁避免线程阻塞。重量级锁Heavyweight Lock当自旋超过一定次数或竞争非常激烈时升级为重量级锁。此时未获取到锁的线程会进入阻塞Blocked状态交给操作系统管理。锁优化补充锁消除JVM的即时编译器JIT在运行时会对一些“不可能存在竞争的锁”进行消除。例如局部变量作为锁对象每个线程都有独立的局部变量无共享JVM会直接删除该synchronized修饰避免不必要的锁开销。锁粗化当多个连续的synchronized代码块使用同一个锁对象时JVM会将这些代码块合并为一个大的同步代码块减少锁的获取/释放次数每次获取/释放锁都有开销。例如循环内多次调用同步方法JVM可能将锁粗化到循环外部。CAS (Compare And Swap比较并交换) 是并发编程中实现原子操作的核心算法是一种乐观锁的实现策略。CAS的工作原理包含三个核心参数内存地址 V (Memory Location)变量在内存中的实际值。期望值 A (Expected Value)线程认为该变量当前应该是什么值。新值 B (New Value)线程想要更新成的值。执行逻辑当且仅当内存地址 V 的值等于期望值 A 时处理器才会将 V 的值更新为 B。否则说明该变量已被其他线程修改当前线程什么都不做通常会进入自旋死循环重试。CAS的优缺点优点非阻塞性CAS 是一种非阻塞算法Non-blocking它不需要像 synchronized 那样挂起和恢复线程。性能高在低、中度竞争的情况下由于减少了线程上下文切换的开销效率远高于重量级锁。缺点循环时间长自旋开销如果高并发下竞争激烈CAS 会频繁失败并不断自旋这会给 CPU 带来巨大的计算压力。只能保证一个共享变量的原子操作对于多个变量的操作仍需使用 synchronized 或 ReentrantLock。ABA 问题最经典的缺点。什么是 ABA 问题如果变量初始值为A在线程1准备修改它的过程中线程2快速地将其改成了B然后又改回了A。 现象线程1观察到值依然是A认为它没变过于是CAS成功。 风险虽然数值没变但变量的状态或对象内部的属性可能已经发生了变化导致逻辑错误。Java提供了AtomicStampedReference类通过引入版本号Stamp来解决 每次变量更新时不仅更新值还增加一个版本号。只有值和版本号都一致CAS才会成功。还可以设置时间戳来解决。CAS 在Java中的实现在Java中CAS主要由 sun.misc.Unsafe 类提供支持。该类中的方法如 compareAndSwapInt是 native 的直接调用硬件底层的指令。ReentrantLock可重入锁ReentrantLock是 Java java.util.concurrent.locks 包下的可重入锁实现基于 AQS抽象队列同步器构建是 synchronized 的 “增强版”—— 既保留了 synchronized 的可重入特性又提供了更灵活的同步控制能力。两者对比:特性synchronizedReentrantLock实现层面JVM 层面关键字由 C 实现JDK 层面API由 Java 编写基于 AQS锁的释放自动释放代码执行完或异常后手动释放必须在 finally 中调用 unlock()灵活性低不可中断无超时机制高支持尝试获取、超时获取、可中断获取公平性只支持非公平锁支持公平锁与非公平锁默认非公平等待队列只能关联 1 个 等待队列wait/notify可以绑定 多个 Condition精细化唤醒ReentrantLock的特有高级功能响应中断 (lockInterruptibly)synchronized一旦进入阻塞等待除非拿到锁否则无法被中断。而ReentrantLock允许线程在等待锁的过程中响应Thread.interrupt()从而避免死等。超时机制 (tryLock)线程可以尝试获取锁如果锁被占用立即返回 false 或者等待一段时间后返回而不是一直阻塞。这在预防死锁时非常有用。公平锁 (Fairness)公平锁按照线程请求锁的顺序分配先到先得。非公平锁默认允许“插队”。如果新来的线程正好碰到锁释放它可以直接抢占性能通常比公平锁高。多个 Condition 对象通过lock.newCondition()你可以创建多个等待集。例如在阻塞队列中可以定义 notFull 和 notEmpty 两个条件实现比 notifyAll 更精准的线程唤醒。基本使用示例import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo { // 创建非公平锁默认若需公平锁new ReentrantLock(true) private static final ReentrantLock lock new ReentrantLock(); public static void doTask() { // 1. 普通获取锁不可中断 lock.lock(); try { // 临界区代码线程安全 System.out.println(Thread.currentThread().getName() 执行任务); Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 必须在finally中释放锁否则锁永远无法释放 lock.unlock(); } } // 超时获取锁示例 public static void tryLockWithTimeout() { try { // 尝试在2秒内获取锁获取成功返回true失败返回false if (lock.tryLock(2, java.util.concurrent.TimeUnit.SECONDS)) { try { System.out.println(Thread.currentThread().getName() 超时获取锁成功); } finally { lock.unlock(); } } else { System.out.println(Thread.currentThread().getName() 超时获取锁失败); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static void main(String[] args) { // 测试普通获取锁 new Thread(ReentrantLockDemo::doTask, 线程1).start(); new Thread(ReentrantLockDemo::doTask, 线程2).start(); // 测试超时获取锁 new Thread(ReentrantLockDemo::tryLockWithTimeout, 线程3).start(); } }