在并发场景中,线程安全的核心是解决多个线程对共享资源的竞态访问问题。以下是多种主流解决方案,按“实现复杂度”和“适用场景”从简单到复杂排序。
一、基础方案:避免共享状态(根本原则)
线程安全问题的根源是“共享资源竞争”,若能从设计上消除共享,可从根本上避免问题。
无状态设计:线程不持有任何共享数据,每次操作仅依赖输入参数和局部变量。
-
示例:Web服务中的无状态控制器(如Spring MVC的Controller),每个请求由独立线程处理,不共享成员变量。
-
线程封闭:将数据封装在单个线程内,仅由该线程访问。
实现方式:
-
局部变量(栈封闭,天然线程安全)。
-
ThreadLocal:为每个线程创建独立的变量副本,如数据库连接池的Connection绑定。
二、锁机制:控制共享资源访问(最常用)
通过“互斥”保证同一时间只有一个线程能操作共享资源,是解决竞态问题的标准方案。
1. 内置锁(Synchronized,Java为例)
-
原理:基于对象监视器(Monitor)实现,自动加锁/释放锁(代码块执行完或抛异常时释放)。
-
适用场景:简单的同步需求,如单例模式、简单的共享变量修改。
示例:
// 同步方法
public synchronized void add() { count++; }
// 同步代码块(锁粒度更细,性能更好)
public void add() {
synchronized (this) { count++; }
}
2. 显式锁(Lock接口,Java为例)
-
原理:手动控制锁的获取(lock())和释放(unlock(),需在finally中执行),功能比synchronized更灵活。
-
核心实现:ReentrantLock(可重入锁,支持公平锁/非公平锁)。
-
适用场景:复杂同步需求,如超时获取锁、中断等待锁、条件变量(Condition)。
示例:
private final Lock lock = new ReentrantLock();
public void add() {
lock.lock();
try { count++; }
finally { lock.unlock(); } // 必须手动释放
}
3. 读写锁(ReadWriteLock,Java为例)
-
原理:区分“读操作”和“写操作”,允许多个线程同时读,但写操作独占(读-写、写-写互斥),提升读多写少场景的性能。
-
核心实现:ReentrantReadWriteLock。
-
适用场景:缓存、配置中心等读操作远多于写操作的场景。
三、原子类:无锁化线程安全(高效轻量)
基于CAS(Compare and Swap,比较并交换) 机制实现,无需加锁,直接通过硬件指令保证操作的原子性,性能优于锁。
适用场景:单个变量的原子性操作(如计数、累加)。
常用类(Java为例):
AtomicInteger/AtomicLong:原子整数/长整数。
AtomicReference:原子引用(支持对象的原子性赋值)。
AtomicStampedReference:解决CAS的“ABA问题”(添加版本号)。
示例:
private AtomicInteger count = new AtomicInteger(0);
public void add() {
count.incrementAndGet(); // 原子性自增,底层是CAS
}
四、并发容器:线程安全的集合类
JDK(或其他语言标准库)提供的线程安全集合,避免手动加锁维护集合操作的安全性。
常用容器(Java为例):
-
ConcurrentHashMap:线程安全的HashMap,采用“分段锁”(JDK7)或“CAS+synchronized”(JDK8)优化,支持高并发读写。
-
CopyOnWriteArrayList:读多写少场景的List,写操作时复制整个数组,读操作无锁(适合读频繁、写极少的场景,如配置列表)。
-
BlockingQueue:阻塞队列,如ArrayBlockingQueue,天然支持生产者-消费者模型,无需手动处理线程唤醒/等待。
五、分布式场景扩展方案
若并发跨多个进程/服务器(分布式系统),本地锁失效,需分布式级别的同步机制:
1.分布式锁:基于Redis(SET NX EX命令)、ZooKeeper(临时顺序节点)实现,保证多个节点对共享资源的互斥访问。
2.分布式事务:如TCC(Try-Confirm-Cancel)、SAGA模式,解决跨服务的数据一致性问题(非直接解决线程安全,而是分布式场景的扩展)。
六、方案选择建议
1.优先无状态:能通过设计避免共享的,优先选择无状态或线程封闭。
2.简单变量用原子类:单个变量的原子操作(如计数),优先用Atomic类,性能最优。
3.复杂同步用显式锁:需要灵活控制锁(如超时、中断)或读多写少场景,用Lock/ReadWriteLock。
4.集合操作选并发容器:避免手动加锁维护ArrayList/HashMap的线程安全,直接用ConcurrentHashMap等。
5.分布式场景用分布式锁:跨进程/服务器的共享资源竞争,需引入分布式锁。