1. 线程安全的核心概念解析
在Java并发编程的世界里,线程安全就像是一个精密的交通控制系统。想象一下,如果十字路口没有红绿灯,车辆随意穿行会发生什么?线程安全就是为多线程程序设计的"红绿灯"系统,确保数据这个"交通流"能够有序通行而不发生"事故"。
1.1 线程安全的本质定义
线程安全的核心在于:当多个执行线程同时访问某个共享资源时,无论这些线程如何调度和交替执行,程序都能产生符合预期的正确结果。这包含三个关键要素:
- 原子性:操作要么完全执行,要么完全不执行,不会出现中间状态
- 可见性:一个线程对共享变量的修改能够立即对其他线程可见
- 有序性:程序执行的顺序符合代码的先后关系(避免指令重排序带来的问题)
注意:线程安全不是绝对的属性,而是相对于特定使用场景而言的。一个类可能在某种使用方式下是线程安全的,而在另一种使用方式下则不是。
1.2 线程不安全的表现形式
让我们通过一个经典的银行转账问题来理解线程不安全可能导致的灾难性后果:
java复制class UnsafeBankAccount {
private int balance;
public void transfer(UnsafeBankAccount to, int amount) {
this.balance -= amount; // 步骤1
to.balance += amount; // 步骤2
}
}
这个简单的转账操作在多线程环境下可能出现以下问题:
- 丢失更新:两个线程同时读取余额,分别修改后写入,导致其中一个修改被覆盖
- 不一致视图:线程A看到步骤1执行后的状态,而线程B看到的是步骤1执行前的状态
- 死锁风险:如果多个转账操作涉及相同的账户对,可能形成循环等待
2. Java内存模型(JMM)与线程安全
2.1 Java内存模型基础
Java内存模型规定了线程如何与内存交互,它定义了以下关键概念:
- 主内存:所有共享变量的存储区域
- 工作内存:每个线程私有的内存区域,存储该线程使用到的变量副本
- 内存屏障:确保特定操作的内存可见性
java复制// volatile变量的典型使用场景
class SharedFlag {
private volatile boolean running = true;
public void stop() {
running = false; // 对其它线程立即可见
}
public void doWork() {
while (running) {
// 执行任务
}
}
}
2.2 happens-before原则
Java内存模型通过happens-before关系来保证内存可见性,主要包括:
- 程序顺序规则:同一线程中的操作,前面的happens-before后面的
- 锁规则:解锁操作happens-before后续的加锁操作
- volatile规则:volatile变量的写操作happens-before后续的读操作
- 线程启动规则:线程A启动线程B,那么A在启动B前的操作对B可见
- 线程终止规则:线程A等待线程B终止,B的所有操作对A可见
3. 实现线程安全的五大策略
3.1 无共享状态设计
最彻底的线程安全方案就是避免共享状态:
java复制class StatelessService {
// 无实例字段,完全依赖方法参数和局部变量
public double calculateInterest(double principal, double rate, int years) {
return principal * Math.pow(1 + rate/100, years) - principal;
}
}
适用场景:
- 纯函数计算
- 无状态服务
- 工具类方法
3.2 不可变对象模式
不可变对象天然线程安全,因为它们的内部状态在创建后就不能被修改:
java复制public final class ImmutablePerson {
private final String name;
private final LocalDate birthDate;
private final List<String> addresses;
public ImmutablePerson(String name, LocalDate birthDate, List<String> addresses) {
this.name = name;
this.birthDate = birthDate;
this.addresses = Collections.unmodifiableList(new ArrayList<>(addresses));
}
// 只有getter方法
public List<String> getAddresses() {
return addresses; // 返回的是不可修改的列表
}
}
实现要点:
- 类声明为final防止子类修改行为
- 所有字段设为final
- 不提供setter方法
- 对可变引用类型进行防御性复制
3.3 同步控制机制
3.3.1 内置锁(synchronized)
java复制class SynchronizedCounter {
private int count;
// 方法级同步
public synchronized void increment() {
count++;
}
// 代码块同步
public void decrement() {
synchronized(this) {
count--;
}
}
}
注意事项:
- 锁对象的选择很重要,通常使用private final对象作为锁
- 避免在同步块中调用外部方法,防止死锁
- 同步范围过大影响性能,过小则无法保证安全
3.3.2 显式锁(ReentrantLock)
java复制class LockBasedCache {
private final Map<String, Object> cache = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();
public void put(String key, Object value) {
lock.lock();
try {
cache.put(key, value);
} finally {
lock.unlock(); // 必须放在finally块中
}
}
}
优势:
- 可中断的锁获取
- 尝试获取锁的超时机制
- 公平锁选项
- 支持多个条件变量
3.4 并发容器
Java并发包(java.util.concurrent)提供了一系列线程安全的容器:
| 接口 | 线程安全实现 | 特点 |
|---|---|---|
| List | CopyOnWriteArrayList | 写时复制,适合读多写少 |
| Map | ConcurrentHashMap | 分段锁/乐观锁,高并发 |
| Queue | ConcurrentLinkedQueue | 无界非阻塞队列 |
| Deque | LinkedBlockingDeque | 有界阻塞双端队列 |
java复制// ConcurrentHashMap的典型使用模式
class ConcurrentCache {
private final ConcurrentMap<String, byte[]> cache = new ConcurrentHashMap<>();
public byte[] get(String key) {
return cache.get(key);
}
public void put(String key, byte[] value) {
cache.put(key, value);
}
// 原子性复合操作
public byte[] putIfAbsent(String key, byte[] value) {
return cache.putIfAbsent(key, value);
}
}
3.5 原子变量类
java.util.concurrent.atomic包提供了一系列原子操作类:
java复制class AtomicInventory {
private final AtomicInteger stock = new AtomicInteger(0);
private final AtomicReference<LocalDate> lastUpdated = new AtomicReference<>();
public void restock(int quantity) {
stock.addAndGet(quantity);
lastUpdated.set(LocalDate.now());
}
public boolean sell(int quantity) {
int current;
do {
current = stock.get();
if (current < quantity) {
return false;
}
} while (!stock.compareAndSet(current, current - quantity));
return true;
}
}
常用原子类:
- AtomicInteger/AtomicLong:整型原子操作
- AtomicReference:引用类型原子操作
- AtomicIntegerArray:数组原子操作
- LongAdder:高并发计数场景
4. 线程安全实战案例分析
4.1 单例模式的线程安全实现
错误实现:
java复制// 非线程安全的懒汉式
class UnsafeSingleton {
private static UnsafeSingleton instance;
private UnsafeSingleton() {}
public static UnsafeSingleton getInstance() {
if (instance == null) {
instance = new UnsafeSingleton(); // 可能被多次初始化
}
return instance;
}
}
正确实现1(双重检查锁定):
java复制class SafeSingleton {
private static volatile SafeSingleton instance;
private SafeSingleton() {}
public static SafeSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (SafeSingleton.class) {
if (instance == null) { // 第二次检查
instance = new SafeSingleton();
}
}
}
return instance;
}
}
正确实现2(静态内部类):
java复制class HolderSingleton {
private HolderSingleton() {}
private static class Holder {
static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return Holder.INSTANCE; // 由JVM保证线程安全
}
}
4.2 线程安全的LRU缓存实现
java复制class ThreadSafeLRUCache<K, V> {
private final int maxSize;
private final ConcurrentHashMap<K, V> map;
private final ConcurrentLinkedDeque<K> queue;
private final ReentrantLock lock = new ReentrantLock();
public ThreadSafeLRUCache(int maxSize) {
this.maxSize = maxSize;
this.map = new ConcurrentHashMap<>(maxSize);
this.queue = new ConcurrentLinkedDeque<>();
}
public V get(K key) {
V value = map.get(key);
if (value != null) {
lock.lock();
try {
queue.remove(key); // 从队列中移除
queue.addFirst(key); // 添加到队首
} finally {
lock.unlock();
}
}
return value;
}
public void put(K key, V value) {
lock.lock();
try {
if (map.size() >= maxSize) {
K oldestKey = queue.removeLast(); // 移除最久未使用的
if (oldestKey != null) {
map.remove(oldestKey);
}
}
map.put(key, value);
queue.remove(key); // 确保key不重复
queue.addFirst(key);
} finally {
lock.unlock();
}
}
}
5. 线程安全性能优化技巧
5.1 减小锁粒度
不推荐做法:
java复制class CoarseLock {
private final Object lock = new Object();
private Map<String, String> users = new HashMap<>();
private Map<String, String> config = new HashMap<>();
public void updateUser(String id, String info) {
synchronized(lock) { // 锁范围过大
users.put(id, info);
}
}
public void updateConfig(String key, String value) {
synchronized(lock) { // 与用户更新互斥,没必要
config.put(key, value);
}
}
}
优化方案:
java复制class FineGrainedLock {
private final Object userLock = new Object();
private final Object configLock = new Object();
private Map<String, String> users = new HashMap<>();
private Map<String, String> config = new HashMap<>();
public void updateUser(String id, String info) {
synchronized(userLock) { // 只锁用户数据
users.put(id, info);
}
}
public void updateConfig(String key, String value) {
synchronized(configLock) { // 只锁配置数据
config.put(key, value);
}
}
}
5.2 读写锁应用
java复制class ReadWriteCache {
private final Map<String, Object> cache = new HashMap<>();
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
public void clear() {
rwLock.writeLock().lock();
try {
cache.clear();
} finally {
rwLock.writeLock().unlock();
}
}
}
5.3 避免锁的过度使用
不推荐做法:
java复制class OverSynchronized {
private final List<String> log = Collections.synchronizedList(new ArrayList<>());
public void addLogEntry(String entry) {
synchronized(log) { // 多余的同步,Collections.synchronizedList已经同步
log.add(entry);
}
}
}
优化方案:
java复制class OptimizedSync {
private final List<String> log = Collections.synchronizedList(new ArrayList<>());
public void addLogEntry(String entry) {
log.add(entry); // 直接使用同步方法
}
// 复合操作才需要额外同步
public boolean addIfAbsent(String entry) {
synchronized(log) {
if (!log.contains(entry)) {
log.add(entry);
return true;
}
return false;
}
}
}
6. 常见线程安全问题排查
6.1 死锁检测与预防
死锁示例:
java复制class DeadlockRisk {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void methodA() {
synchronized(lock1) {
synchronized(lock2) { // 可能死锁
// 操作共享资源
}
}
}
public void methodB() {
synchronized(lock2) {
synchronized(lock1) { // 与methodA相反的锁顺序
// 操作共享资源
}
}
}
}
预防措施:
- 统一锁的获取顺序
- 使用tryLock()设置超时
- 通过jstack或VisualVM检测死锁
- 避免在持有锁时调用外部方法
6.2 活锁与资源耗尽
活锁示例:
java复制class LiveLockExample {
private boolean resourceBusy = false;
public void accessResource() {
while (true) {
if (!resourceBusy) {
resourceBusy = true;
try {
// 使用资源
return;
} finally {
resourceBusy = false;
}
} else {
Thread.yield(); // 过度谦让导致活锁
}
}
}
}
解决方案:
- 引入随机退避机制
- 使用阻塞队列管理资源请求
- 限制重试次数
6.3 性能问题诊断
诊断工具:
- JVisualVM:监控线程状态和锁竞争
- JStack:生成线程转储分析锁持有情况
- Java Mission Control:详细性能分析
- 基准测试:JMH框架进行并发性能测试
优化指标:
- 锁竞争频率
- 线程等待时间
- 上下文切换次数
- CPU利用率
7. Java并发工具进阶
7.1 CountDownLatch应用
java复制class ParallelProcessor {
private final int threadCount;
private final CountDownLatch latch;
public ParallelProcessor(int threadCount) {
this.threadCount = threadCount;
this.latch = new CountDownLatch(threadCount);
}
public void process(List<Data> dataList) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
int batchSize = dataList.size() / threadCount;
for (int i = 0; i < threadCount; i++) {
int start = i * batchSize;
int end = (i == threadCount - 1) ? dataList.size() : start + batchSize;
List<Data> subList = dataList.subList(start, end);
executor.execute(() -> {
try {
processBatch(subList);
} finally {
latch.countDown(); // 完成一个批次
}
});
}
latch.await(); // 等待所有批次完成
executor.shutdown();
}
private void processBatch(List<Data> batch) {
// 处理数据批次
}
}
7.2 CyclicBarrier使用场景
java复制class MatrixMultiplier {
private final int size;
private final double[][] matrixA;
private final double[][] matrixB;
private final double[][] result;
private final CyclicBarrier barrier;
public MatrixMultiplier(double[][] a, double[][] b) {
this.matrixA = a;
this.matrixB = b;
this.size = a.length;
this.result = new double[size][size];
this.barrier = new CyclicBarrier(size, () -> mergeResults());
}
public double[][] multiply() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(size);
for (int i = 0; i < size; i++) {
final int row = i;
executor.execute(() -> {
for (int col = 0; col < size; col++) {
double sum = 0;
for (int k = 0; k < size; k++) {
sum += matrixA[row][k] * matrixB[k][col];
}
result[row][col] = sum;
}
try {
barrier.await(); // 等待所有行处理完成
} catch (Exception e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS);
return result;
}
private void mergeResults() {
// 所有行计算完成后的处理
}
}
7.3 CompletableFuture组合异步操作
java复制class AsyncService {
private final ExecutorService executor = Executors.newFixedThreadPool(4);
public CompletableFuture<Result> processAsync(Request request) {
return CompletableFuture.supplyAsync(() -> validate(request), executor)
.thenApplyAsync(this::parse, executor)
.thenCombineAsync(
fetchRemoteData(request),
(parsed, remote) -> combine(parsed, remote),
executor
)
.exceptionally(ex -> handleError(ex));
}
private ValidationResult validate(Request req) { /*...*/ }
private ParsedData parse(ValidationResult v) { /*...*/ }
private CompletableFuture<RemoteData> fetchRemoteData(Request req) { /*...*/ }
private Result combine(ParsedData p, RemoteData r) { /*...*/ }
private Result handleError(Throwable t) { /*...*/ }
}
8. 现代Java并发特性
8.1 Java 8的并发增强
并行流处理:
java复制List<Data> processInParallel(List<Data> input) {
return input.parallelStream()
.filter(this::isValid)
.map(this::transform)
.collect(Collectors.toList());
}
CompletableFuture组合:
java复制CompletableFuture<Result> fetchData() {
return CompletableFuture.supplyAsync(this::loadFromCache)
.thenApplyAsync(this::enrichFromDB)
.thenCombine(fetchFromAPI(), this::mergeResults);
}
8.2 Java 9+的并发改进
响应式流支持:
java复制Flow.Publisher<Data> createPublisher(List<Data> data) {
return subscriber -> {
subscriber.onSubscribe(new Flow.Subscription() {
// 实现背压控制
});
// 发布数据
};
}
VarHandle内存访问:
java复制class AtomicCounter {
private static final VarHandle COUNT;
private volatile int count;
static {
try {
COUNT = MethodHandles.lookup()
.findVarHandle(AtomicCounter.class, "count", int.class);
} catch (Exception e) {
throw new Error(e);
}
}
public void increment() {
COUNT.getAndAdd(this, 1); // 比AtomicInteger更轻量
}
}
8.3 虚拟线程(Project Loom)
java复制void handleRequests(List<Request> requests) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
requests.forEach(request ->
executor.submit(() -> processRequest(request)));
}
}
优势:
- 轻量级线程,数量可达百万级
- 低上下文切换开销
- 简化高并发编程模型
- 兼容现有Thread API