💡 摘要:你是否遇到过 i++ 在多线程下结果不正确?
是否在 synchronizedReentrantLock 之间犹豫不决?
Java 多线程同步机制远不止 synchronized
JVM 内置锁JUC 并发包
悲观锁乐观锁
volatile 内存语义CAS 无锁算法
本文将带你系统梳理 Java 同步的 8 大机制
深入剖析 synchronized 的锁升级过程、
ReentrantLock 的 AQS 实现原理、
CountDownLatchCyclicBarrier 的区别,
并结合 ThreadLocal 解决线程安全问题。
文末附同步机制选型决策树面试高频问题
助你彻底掌握并发编程的核心武器。


一、为什么需要线程同步?

多线程环境下,共享资源的并发访问会导致数据不一致

经典问题:i++ 的线程安全

public class Counter {
    private int i = 0;
    
    public void increment() {
        i++; // 非原子操作!
    }
    
    public int get() {
        return i;
    }
}

// 多线程调用
Counter counter = new Counter();
ExecutorService executor = Executors.newFixedThreadPool(10);

for (int i = 0; i < 1000; i++) {
    executor.submit(counter::increment);
}

executor.shutdown();
// 最终结果很可能 < 1000!

i++ 为什么不是原子的?
它包含 3 个步骤:读取 ii+1 → 写回 i
多个线程可能同时读取到相同的 i 值,导致丢失更新


二、Java 同步机制概览

机制 类型 特点 适用场景
1. synchronized 关键字 悲观锁 JVM 内置,简单 方法/代码块同步
2. ReentrantLock 悲观锁 功能丰富,可中断 高级锁控制
3. volatile 关键字 内存可见性 轻量级,无锁 状态标志、单例
4. Atomic 类 (CAS) 乐观锁 无锁,高性能 计数器、状态更新
5. ThreadLocal 线程隔离 每线程副本 数据库连接、用户信息
6. 显式 Lock 配合 Condition 条件等待 精确唤醒 生产者-消费者
7. 并发集合 (ConcurrentHashMap) 安全容器 分段锁/CAS 高并发读写
8. 同步工具类 (CountDownLatch) 协调线程 屏障、计数 线程协作

1. synchronized 关键字(JVM 内置锁)

✅ 语法

// 1. 修饰实例方法:锁当前实例 (this)
public synchronized void method1() { /* ... */ }

// 2. 修饰静态方法:锁类对象 (Counter.class)
public static synchronized void method2() { /* ... */ }

// 3. 修饰代码块:锁指定对象
public void method3() {
    synchronized (this) { // 或 synchronized (lockObject)
        // 同步代码
    }
}

✅ 特性

  • 可重入:同一线程可多次获取同一把锁。
  • 自动释放:异常时也会自动释放锁。
  • 非公平锁:不保证等待线程的获取顺序。

🔬 synchronized 的锁升级(JDK 1.6+ 优化)

为了减少重量级锁的开销,JVM 实现了锁升级:

  1. 偏向锁:偏向第一个获取锁的线程,后续该线程进入同步块无需再同步。
  2. 轻量级锁:当有第二个线程竞争时,升级为轻量级锁(自旋)。
  3. 重量级锁:竞争激烈时,升级为操作系统互斥量(Mutex),线程阻塞。

优点:简单、安全、JVM 优化好。
⚠️ 缺点:不可中断、不支持超时、不支持条件变量。


2. ReentrantLock(可重入锁)

✅ 语法

private final ReentrantLock lock = new ReentrantLock();

public void method() {
    lock.lock(); // 获取锁
    try {
        // 同步代码
    } finally {
        lock.unlock(); // 必须在 finally 中释放!
    }
}

✅ 优势(相比 synchronized

  1. 可中断lockInterruptibly() 可响应中断。
  2. 可超时tryLock(timeout) 尝试获取,超时失败。
  3. 公平锁new ReentrantLock(true) 可设置为公平锁。
  4. 多个条件变量:配合 Condition 实现精确唤醒。

✅ 示例:公平锁与超时

ReentrantLock fairLock = new ReentrantLock(true); // 公平锁

public void timedOperation() throws InterruptedException {
    if (fairLock.tryLock(3, TimeUnit.SECONDS)) {
        try {
            // 操作
        } finally {
            fairLock.unlock();
        }
    } else {
        System.out.println("获取锁超时");
    }
}

⚠️ 注意:必须在 finally 块中调用 unlock(),否则可能导致死锁。


3. volatile 关键字(内存可见性)

✅ 作用

  • 保证可见性:一个线程修改了 volatile 变量,其他线程能立即看到。
  • 禁止指令重排序:保证代码执行顺序。

✅ 适用场景

  1. 状态标志:控制线程的生命周期。
private volatile boolean running = true;

public void run() {
    while (running) {
        // 执行任务
    }
}

public void shutdown() {
    running = false; // 其他线程可立即看到
}
  1. 双重检查锁定(DCL)单例
public class Singleton {
    private static volatile Singleton instance;
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

⚠️ 注意volatile 不保证原子性
volatile int i; i++ 仍然是线程不安全的。


4. Atomic 类(CAS 无锁算法)

✅ 核心:CAS (Compare-And-Swap)

  • AtomicIntegerAtomicLongAtomicReference 等。
  • 基于 CPU 的 cmpxchg 指令实现。
  • 使用 Unsafe 类进行原子操作。

✅ 示例

private AtomicInteger counter = new AtomicInteger(0);

public void increment() {
    counter.incrementAndGet(); // 原子自增
}

public int get() {
    return counter.get();
}

✅ ABA 问题与 AtomicStampedReference

CAS 可能出现 ABA 问题:值从 A→B→A,CAS 认为未变,但实际已变。

// 使用版本号解决 ABA
AtomicStampedReference<Integer> stampedRef = 
    new AtomicStampedReference<>(100, 0);

int[] stampHolder = new int[1];
Integer oldVal = stampedRef.get(stampHolder);
int stamp = stampHolder[0];

// 带版本号的 CAS
boolean success = stampedRef.compareAndSet(oldVal, newVal, stamp, stamp + 1);

优点:无锁,性能高,适合高并发计数。
⚠️ 缺点:高竞争下可能“自旋”消耗 CPU。


5. ThreadLocal(线程局部变量)

✅ 作用

为每个线程提供独立的变量副本,避免共享。

✅ 示例:数据库连接管理

public class ConnectionHolder {
    private static final ThreadLocal<Connection> connectionHolder = 
        new ThreadLocal<Connection>() {
            @Override
            protected Connection initialValue() {
                return DriverManager.getConnection(URL);
            }
        };
    
    public static Connection getConnection() {
        return connectionHolder.get();
    }
    
    public static void remove() {
        connectionHolder.remove(); // 防止内存泄漏!
    }
}

// 在同一线程中,多次调用 getConnection() 返回同一个连接
Connection conn1 = ConnectionHolder.getConnection();
Connection conn2 = ConnectionHolder.getConnection(); // == conn1

⚠️ 重要:使用完后务必调用 remove(),否则在线程池环境下可能导致内存泄漏


6. Condition(条件变量)

✅ 作用

配合 ReentrantLock,实现精确的线程等待与唤醒

✅ 示例:生产者-消费者

public class BoundedBuffer {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    
    private final Object[] items = new Object[100];
    private int putIndex, takeIndex, count;
    
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await(); // 等待不满
            }
            items[putIndex] = x;
            if (++putIndex == items.length) putIndex = 0;
            ++count;
            notEmpty.signal(); // 唤醒消费者
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // 等待不空
            }
            Object x = items[takeIndex];
            if (++takeIndex == items.length) takeIndex = 0;
            --count;
            notFull.signal(); // 唤醒生产者
            return x;
        } finally {
            lock.unlock();
        }
    }
}

优势:比 Object.wait()/notify() 更灵活,可创建多个 Condition


7. 并发集合

✅ ConcurrentHashMap

  • 线程安全的 HashMap
  • JDK 1.8+ 使用 CAS + synchronized,锁粒度更细。
  • 读操作不加锁,写操作锁桶。
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
Integer value = map.get("key");

✅ CopyOnWriteArrayList

  • 写时复制,读操作无锁。
  • 适合读多写少的场景。
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item");
list.get(0);

8. 同步工具类

✅ CountDownLatch(倒计时门闩)

  • 等待一组操作完成。
CountDownLatch latch = new CountDownLatch(3);

for (int i = 0; i < 3; i++) {
    executor.submit(() -> {
        // 执行任务
        latch.countDown(); // 计数减一
    });
}

latch.await(); // 等待计数为0
System.out.println("所有任务完成!");

✅ CyclicBarrier(循环屏障)

  • 让一组线程互相等待,到达屏障点后一起继续。
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有线程已到达屏障,开始下一阶段");
});

for (int i = 0; i < 3; i++) {
    executor.submit(() -> {
        // 阶段1
        barrier.await(); // 等待其他线程
        // 阶段2
    });
}

✅ Semaphore(信号量)

  • 控制同时访问特定资源的线程数量。
Semaphore semaphore = new Semaphore(3); // 同时允许3个线程

semaphore.acquire(); // 获取许可
try {
    // 访问资源
} finally {
    semaphore.release(); // 释放许可
}

三、同步机制选型决策树


四、面试高频问题

❓1. synchronized 和 ReentrantLock 的区别?

  • synchronized:JVM 内置,简单,自动释放,非公平。
  • ReentrantLock:API 级,功能丰富(可中断、超时、公平锁),需手动释放。

❓2. volatile 能保证原子性吗?

不能
volatile 只保证可见性禁止重排序
i++ 这样的复合操作仍需 synchronizedAtomicInteger


❓3. 什么是 CAS?有什么缺点?

  • CAS:Compare-And-Swap,乐观锁基础。
  • 缺点
    1. ABA 问题(可用 AtomicStampedReference 解决)
    2. 自旋消耗 CPU(高竞争下)
    3. 只能保证单个变量的原子性

❓4. ThreadLocal 会导致内存泄漏吗?

可能
ThreadLocalEntryWeakReference,但值是强引用
如果 ThreadLocal 实例被回收,但线程(如线程池中的线程)长期运行,
Entry 的键为 null,但值仍存在,导致内存泄漏
解决:使用完后调用 remove()


❓5. synchronized 锁升级的过程?

  1. 无锁
  2. 偏向锁:偏向第一个线程。
  3. 轻量级锁:自旋,避免阻塞。
  4. 重量级锁:阻塞,依赖操作系统。

五、总结

机制 推荐度 说明
synchronized 简单场景首选
ReentrantLock ✅✅ 需要高级功能时
volatile 状态标志、DCL
Atomic 类 ✅✅ 高并发计数
ThreadLocal 线程隔离
Condition 精确唤醒
并发集合 ✅✅ 高并发容器
同步工具类 线程协作

终极建议

  1. 优先使用 synchronized,简单有效。
  2. 高并发计数用 Atomic
  3. 状态标志用 volatile
  4. 复杂协作用 ReentrantLock + Condition
  5. 避免过度同步,考虑并发集合和无锁算法。

掌握这些同步机制,你就能从容应对 Java 并发编程的挑战!

Logo

惟楚有才,于斯为盛。欢迎来到长沙!!! 茶颜悦色、臭豆腐、CSDN和你一个都不能少~

更多推荐