竞态条件
某个计算的正确性取决于多个线程交替执行的时序。
常见的竞态条件有两种
- 读-改-写
- 检查-执行
读-改-写
当n个线程并发调用selfCount方法时,cuont最后的结果很大概率小于n。
public class ReadUpdateWrite {
private int count = 0;
public void selfCount() {
count++;
}
}
检查-执行
在多线程并发环境下,instance很有可能被实例化多次,从而引发不可预测的错误。
public class LazyInitRace {
private Object instance = null;
public Object getInstance() {
if (instance == null) {
instance = new Object();
}
return instance;
}
}
原子性
原子操作
假定有操作A和操作B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说是原子的。
原子操作是指,以原子方式执行访问同一个状态的所有操作。换句话说,操作具有原子性。
原子操作意义
解决竞态条件导致的线程安全问题。
如何实现原子操作
实现原子操作,最直观最简单的方法就是加锁。
Java中的锁实现有内置锁synchronized和显式锁Lock。
上面的两个例子,使用内置锁进行改造如下,即可保证线程安全。
public class ReadUpdateWrite {
private int count = 0;
public synchronized void selfCount() {
count++;
}
}
public class LazyInitRace {
private Object instance = null;
public synchronized Object getInstance() {
if (instance == null) {
instance = new Object();
}
return instance;
}
}