核心概念
线程同步是指协调多个线程,确保共享资源访问的安全性和有序性。Java提供了多种同步机制,从底层到高层、从重量级到轻量级。
Java线程同步的8种方式
对比速览
| 方式 | 类型 | 粒度 | 性能 | 适用场景 |
|---|---|---|---|---|
| synchronized | 内置锁 | 粗粒度 | 中 | 通用同步 |
| Lock | 显式锁 | 细粒度 | 高 | 复杂同步逻辑 |
| volatile | 内存可见性 | 轻量级 | 最高 | 状态标志 |
| 原子类 | CAS操作 | 轻量级 | 高 | 计数器、累加器 |
| ThreadLocal | 线程隔离 | 无竞争 | 高 | 线程私有数据 |
| 信号量 | 许可证机制 | 灵活 | 中 | 资源池限流 |
| CountDownLatch | 一次性同步 | 计数器 | 中 | 等待多线程完成 |
| CyclicBarrier | 循环同步 | 栅栏 | 中 | 分阶段任务 |
1. synchronized(内置锁)
基本用法
public class SynchronizedDemo {
private int count = 0;
// 1. 同步方法
public synchronized void incrementMethod() {
count++;
}
// 2. 同步代码块
public void incrementBlock() {
synchronized (this) {
count++;
}
}
// 3. 同步静态方法(类锁)
public static synchronized void staticMethod() {
// 锁的是类对象
}
// 4. 同步静态代码块
public void staticBlock() {
synchronized (SynchronizedDemo.class) {
// 锁的是类对象
}
}
}
原理
// 字节码层面
public synchronized void increment();
flags: ACC_PUBLIC, ACC_SYNCHRONIZED // 方法标志
Code:
// ...
public void incrementBlock();
Code:
monitorenter // 进入监视器(获取锁)
// ... 业务代码
monitorexit // 退出监视器(释放锁)
特点:
- 可重入锁(同一线程可多次获取)
- 自动释放(异常时也会释放)
- 对象头Mark Word存储锁信息
- JDK 6+引入锁优化(偏向锁、轻量级锁、自旋锁)
适用场景
// ✅ 简单的同步场景
public class Counter {
private int count = 0;
public synchronized int incrementAndGet() {
return ++count;
}
}
2. Lock(显式锁)
ReentrantLock
public class LockDemo {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 显式加锁
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放
}
}
// 尝试加锁(非阻塞)
public boolean tryIncrement() {
if (lock.tryLock()) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
// 超时加锁
public boolean tryIncrementWithTimeout(long timeout, TimeUnit unit)
throws InterruptedException {
if (lock.tryLock(timeout, unit)) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
// 可中断加锁
public void interruptibleIncrement() throws InterruptedException {
lock.lockInterruptibly();
try {
count++;
} finally {
lock.unlock();
}
}
}
ReadWriteLock(读写锁)
public class ReadWriteLockDemo {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private int count = 0;
// 读操作(多个线程可同时读)
public int getCount() {
readLock.lock();
try {
return count;
} finally {
readLock.unlock();
}
}
// 写操作(独占)
public void increment() {
writeLock.lock();
try {
count++;
} finally {
writeLock.unlock();
}
}
}
Lock vs synchronized:
| 特性 | synchronized | Lock |
|---|---|---|
| 锁获取 | 自动 | 手动 |
| 锁释放 | 自动 | 手动(finally) |
| 尝试加锁 | ❌ | ✅ tryLock() |
| 超时加锁 | ❌ | ✅ tryLock(timeout) |
| 可中断 | ❌ | ✅ lockInterruptibly() |
| 公平性 | ❌ | ✅ 可选 |
| 条件变量 | 1个(wait/notify) | 多个(Condition) |
适用场景
// ✅ 需要高级功能
public class AdvancedLocking {
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private final Object[] items = new Object[100];
private int count, putIndex, takeIndex;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await(); // 等待notFull条件
items[putIndex] = x;
if (++putIndex == items.length) putIndex = 0;
++count;
notEmpty.signal(); // 通知notEmpty条件
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await(); // 等待notEmpty条件
Object x = items[takeIndex];
if (++takeIndex == items.length) takeIndex = 0;
--count;
notFull.signal(); // 通知notFull条件
return x;
} finally {
lock.unlock();
}
}
}
3. volatile(内存可见性)
基本用法
public class VolatileDemo {
private volatile boolean running = true; // 保证可见性
public void start() {
new Thread(() -> {
while (running) {
// 执行任务
}
System.out.println("线程停止");
}).start();
}
public void stop() {
running = false; // 修改立即对其他线程可见
}
}
原理
// volatile的两个作用:
1. 保证可见性:修改立即刷新到主内存,读取从主内存读取
2. 禁止指令重排序:使用内存屏障
// 底层实现(x86):
// 写操作:在写后插入StoreLoad屏障(lock指令前缀)
movl $1, running // 写入volatile变量
lock addl $0, (%esp) // 内存屏障(强制刷新)
特点:
- 轻量级同步
- 不保证原子性(除赋值外)
- 适合单线程写、多线程读
volatile的局限性
// ❌ volatile不保证复合操作的原子性
public class VolatileCounter {
private volatile int count = 0;
public void increment() {
count++; // 非原子操作:读取 → 计算 → 写入
}
}
// ✅ 使用原子类
public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 原子操作
}
}
适用场景
// ✅ 状态标志
private volatile boolean shutdown = false;
// ✅ 双重检查锁定(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(); // volatile防止重排序
}
}
}
return instance;
}
}
// ✅ 一次性安全发布
private volatile Map<String, String> config;
public void updateConfig() {
Map<String, String> newConfig = loadConfig();
config = newConfig; // 安全发布
}
4. 原子类(Atomic)
基本类型原子类
public class AtomicDemo {
// 原子整数
private AtomicInteger atomicInt = new AtomicInteger(0);
// 原子长整数
private AtomicLong atomicLong = new AtomicLong(0L);
// 原子布尔
private AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public void demonstrate() {
// CAS操作
atomicInt.incrementAndGet(); // ++i
atomicInt.getAndIncrement(); // i++
atomicInt.addAndGet(5); // i += 5
atomicInt.compareAndSet(0, 1); // if (i == 0) i = 1;
// 函数式更新
atomicInt.updateAndGet(x -> x * 2); // i = i * 2
atomicInt.accumulateAndGet(5, (x, y) -> x + y); // i = i + 5
}
}
数组原子类
public class AtomicArrayDemo {
private AtomicIntegerArray array = new AtomicIntegerArray(10);
public void increment(int index) {
array.incrementAndGet(index); // 原子递增指定位置
}
public int get(int index) {
return array.get(index);
}
}
引用原子类
public class AtomicReferenceDemo {
static class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
}
private AtomicReference<User> atomicUser =
new AtomicReference<>(new User("Tom", 20));
public void updateUser() {
User oldUser = atomicUser.get();
User newUser = new User("Jerry", 21);
atomicUser.compareAndSet(oldUser, newUser); // CAS更新
}
}
字段原子更新器
public class AtomicFieldDemo {
// 原子更新int字段
private static final AtomicIntegerFieldUpdater<User> ageUpdater =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
static class User {
String name;
volatile int age; // 必须是volatile
User(String name, int age) {
this.name = name;
this.age = age;
}
}
public void updateAge(User user, int newAge) {
ageUpdater.compareAndSet(user, user.age, newAge);
}
}
累加器(JDK 8+)
public class AccumulatorDemo {
// 高性能计数器(分段CAS)
private LongAdder counter = new LongAdder();
public void increment() {
counter.increment(); // 比AtomicLong更快
}
public long getCount() {
return counter.sum();
}
}
LongAdder vs AtomicLong:
AtomicLong:单个变量CAS,高竞争下性能差LongAdder:分段累加,高竞争下性能好
5. ThreadLocal(线程隔离)
基本用法
public class ThreadLocalDemo {
private static ThreadLocal<Integer> threadLocal =
ThreadLocal.withInitial(() -> 0);
public void increment() {
int value = threadLocal.get();
threadLocal.set(value + 1);
}
public int getValue() {
return threadLocal.get();
}
public static void main(String[] args) throws InterruptedException {
ThreadLocalDemo demo = new ThreadLocalDemo();
Thread t1 = new Thread(() -> {
demo.increment();
demo.increment();
System.out.println("线程1: " + demo.getValue()); // 2
});
Thread t2 = new Thread(() -> {
demo.increment();
System.out.println("线程2: " + demo.getValue()); // 1
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
典型应用
// 1. 数据库连接管理
public class ConnectionManager {
private static ThreadLocal<Connection> connectionHolder =
ThreadLocal.withInitial(() -> {
try {
return DriverManager.getConnection("jdbc:mysql://...");
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
public static Connection getConnection() {
return connectionHolder.get();
}
public static void closeConnection() {
Connection conn = connectionHolder.get();
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
connectionHolder.remove(); // 防止内存泄漏
}
}
}
}
// 2. 用户上下文
public class UserContext {
private static ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setUser(User user) {
currentUser.set(user);
}
public static User getUser() {
return currentUser.get();
}
public static void clear() {
currentUser.remove();
}
}
// 3. SimpleDateFormat线程安全
public class DateUtil {
private static ThreadLocal<SimpleDateFormat> formatter =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public static String format(Date date) {
return formatter.get().format(date);
}
}
注意事项
// ❌ 内存泄漏风险
public class ThreadLocalLeak {
private static ThreadLocal<byte[]> local = new ThreadLocal<>();
public void allocate() {
local.set(new byte[1024 * 1024 * 10]); // 10MB
// 未调用remove(),线程池中线程不销毁,导致内存泄漏
}
}
// ✅ 正确使用
public class ThreadLocalCorrect {
private static ThreadLocal<Resource> local = new ThreadLocal<>();
public void process() {
try {
local.set(new Resource());
// ... 使用资源
} finally {
local.remove(); // 及时清理
}
}
}
6. Semaphore(信号量)
public class SemaphoreDemo {
// 限制同时访问的线程数为3
private final Semaphore semaphore = new Semaphore(3);
public void访问资源() throws InterruptedException {
semaphore.acquire(); // 获取许可证
try {
System.out.println(Thread.currentThread().getName() + " 访问资源");
Thread.sleep(1000);
} finally {
semaphore.release(); // 释放许可证
}
}
public static void main(String[] args) {
SemaphoreDemo demo = new SemaphoreDemo();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
demo.访问资源();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread-" + i).start();
}
}
}
适用场景:
- 连接池限流
- 资源访问控制
- 限流器实现
7. CountDownLatch(倒计数门闩)
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int workerCount = 5;
CountDownLatch latch = new CountDownLatch(workerCount);
// 启动5个工作线程
for (int i = 0; i < workerCount; i++) {
int workerId = i;
new Thread(() -> {
System.out.println("Worker " + workerId + " 开始工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("Worker " + workerId + " 完成工作");
latch.countDown(); // 计数减1
}).start();
}
System.out.println("主线程等待所有Worker完成...");
latch.await(); // 阻塞直到计数为0
System.out.println("所有Worker已完成,主线程继续");
}
}
8. CyclicBarrier(循环栅栏)
public class CyclicBarrierDemo {
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("所有线程已到达屏障,开始下一阶段");
});
for (int i = 0; i < threadCount; i++) {
int threadId = i;
new Thread(() -> {
try {
System.out.println("线程" + threadId + " 阶段1完成");
barrier.await(); // 等待其他线程
System.out.println("线程" + threadId + " 阶段2完成");
barrier.await(); // 可重复使用
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
答题总结
面试标准答案:
Java线程同步主要有8种方式:
1. synchronized(内置锁)
- 简单易用,自动释放
- 适合简单同步场景
2. Lock(显式锁)
- 功能丰富:tryLock、超时、可中断
- 适合复杂同步逻辑
3. volatile(可见性)
- 轻量级,保证可见性和有序性
- 适合状态标志、单写多读
4. 原子类(Atomic)
- CAS无锁,高性能
- 适合计数器、累加器
5. ThreadLocal(线程隔离)
- 无竞争,线程私有
- 适合连接管理、上下文传递
6. Semaphore(信号量)
- 许可证机制
- 适合资源池、限流
7. CountDownLatch(倒计数)
- 一次性同步
- 适合等待多线程完成
8. CyclicBarrier(循环栅栏)
- 可重复使用
- 适合分阶段任务
选择建议:
- 简单场景:synchronized
- 复杂逻辑:Lock + Condition
- 高性能计数:LongAdder
- 状态标志:volatile
- 线程私有:ThreadLocal