✨ 1. 什么是线程安全?
线程安全指的是:当多个线程同时访问同一块代码时,无论运行时环境采用怎样的调度方式或者这些线程将怎样交替执行,代码的行为都能正确执行,且不会出现数据不一致、脏数据或异常崩溃。
举个简单例子:
// 非线程安全示例
private int count = 0;public void increment() {count++;
}
多线程同时调用 increment()
,因为 count++
不是原子操作,可能导致最终结果不正确。
🔥 2. 保证线程安全的几种常见方式
方式 | 核心思路 | 适用场景 |
---|---|---|
使用同步机制(synchronized) | 控制同一时刻只能有一个线程访问关键代码区 | 轻量级同步,竞争不激烈时 |
使用显式锁(ReentrantLock) | 手动加锁和释放,支持更灵活的锁粒度控制 | 有锁超时、可中断、读写锁需求时 |
使用原子类(AtomicInteger 等) | 利用底层 CAS 实现无锁线程安全操作 | 简单计数器、自增器 |
使用线程安全容器(如 ConcurrentHashMap) | 内部已经实现了并发控制 | 需要高并发访问集合时 |
局部变量 | 每个线程有自己独立的数据,无共享风险 | 临时计算或业务无状态场景 |
ThreadLocal 机制 | 为每个线程提供独立变量副本,避免共享冲突 | 保存用户信息、请求上下文 |
无状态设计(Stateless) | 类或方法不维护任何可变状态,不存在数据竞争问题 | 纯逻辑计算、工具类 |
🎯 3. 示例理解
3.1 使用 synchronized
public synchronized void increment() {count++;
}
或者锁住特定代码块:
public void increment() {synchronized(this) {count++;}
}
🔵 注意:synchronized属于悲观锁,性能有一定损耗,适合简单场景。
3.2 使用 ReentrantLock
private final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}
}
🔵 支持更丰富的功能,比如可中断锁、尝试加锁、可重入。
3.3 使用原子类 AtomicInteger
private final AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet();
}
🔵 基于 CAS (Compare And Swap) 算法实现,无锁,适合高并发下的简单操作。
3.4 使用线程安全容器
private final Map<String, String> map = new ConcurrentHashMap<>();
🔵 适合高并发读写,比如缓存、用户会话存储。
3.5 使用 ThreadLocal
private ThreadLocal<Integer> threadLocalCount = ThreadLocal.withInitial(() -> 0);public void increment() {threadLocalCount.set(threadLocalCount.get() + 1);
}
🔵 每个线程有自己独立的 count
,互不干扰,非常适合每个线程独立上下文数据。
📊 总结一下
用一张 Mermaid 流程图来快速理解选用策略:
🧠 面试常见延伸问题
- synchronized 和 Lock 的区别?
- CAS 的原理?CAS 会有什么问题?如何解决?
- 为什么推荐尽可能使用无锁编程(Lock-Free Programming)?