Java线程安全
外观
简介[编辑 | 编辑源代码]
线程安全是Java多线程编程中的核心概念,指当多个线程同时访问某个类、对象或方法时,系统能确保其行为始终符合设计预期,不会因线程调度顺序而产生数据不一致或逻辑错误。线程安全的本质是通过同步机制控制对共享资源的并发访问。
为什么需要线程安全[编辑 | 编辑源代码]
当多个线程访问共享数据时,可能引发以下问题:
- 竞态条件(Race Condition):线程执行顺序影响最终结果
- 内存可见性(Memory Visibility):线程修改对其他线程不可见
- 指令重排序(Instruction Reordering):编译器/处理器优化导致意外行为
线程安全实现方式[编辑 | 编辑源代码]
1. 不可变对象[编辑 | 编辑源代码]
最简单的方式是使用不可变(Immutable)对象:
public final class ImmutableValue {
private final int value;
public ImmutableValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
2. 同步方法[编辑 | 编辑源代码]
使用synchronized
关键字修饰方法:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
3. 同步代码块[编辑 | 编辑源代码]
更细粒度的控制:
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
4. volatile 关键字[编辑 | 编辑源代码]
解决可见性问题:
public class SharedObject {
private volatile int sharedCounter = 0;
}
5. 原子类[编辑 | 编辑源代码]
java.util.concurrent.atomic
包提供的原子类:
AtomicInteger atomicInt = new AtomicInteger(0);
int newValue = atomicInt.incrementAndGet(); // 原子操作
6. 线程安全集合[编辑 | 编辑源代码]
Java并发包提供的线程安全集合:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
线程安全级别[编辑 | 编辑源代码]
根据安全程度可分为:
安全级别 | 描述 | 示例 |
---|---|---|
不可变 | 对象完全不可变 | String
|
无条件安全 | 内部同步保证安全 | ConcurrentHashMap
|
有条件安全 | 需要正确使用才能安全 | Collections.synchronizedList
|
非线程安全 | 完全不保证安全 | ArrayList
|
死锁与避免[编辑 | 编辑源代码]
四个必要条件: 1. 互斥条件 2. 请求与保持 3. 不可剥夺 4. 循环等待
避免策略:
- 加锁顺序一致
- 使用定时锁(
tryLock
) - 死锁检测
性能考量[编辑 | 编辑源代码]
同步会带来性能开销,需权衡:
- 减少同步范围
- 使用读写锁(
ReentrantReadWriteLock
) - 考虑无锁编程
实际案例[编辑 | 编辑源代码]
银行转账问题[编辑 | 编辑源代码]
public class BankAccount {
private double balance;
public synchronized void deposit(double amount) {
balance += amount;
}
public synchronized void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
}
}
// 必须同步整个转账过程
public static void transfer(BankAccount from,
BankAccount to,
double amount) {
synchronized(from) {
synchronized(to) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
}
常见误区[编辑 | 编辑源代码]
- 认为
volatile
能解决所有同步问题 - 过度同步导致性能下降
- 忽略复合操作的非原子性
- 误用线程安全集合的迭代器
数学原理[编辑 | 编辑源代码]
线程安全本质是维护不变式(Invariants)。对于共享变量,其不变式可以表示为:
其中是所有线程的集合。
最佳实践[编辑 | 编辑源代码]
- 优先使用不可变对象
- 缩小同步范围
- 文档记录线程安全策略
- 使用高级并发工具(
ExecutorService
,CountDownLatch
等) - 避免在同步块中调用外部方法
总结[编辑 | 编辑源代码]
Java线程安全是多线程编程的基础,理解不同实现方式的适用场景和性能影响至关重要。开发者应根据具体需求选择合适的同步策略,并通过测试验证线程安全性。