竞态条件(Race Condition)
- ++count这个操作是紧凑的语法,但并非原子的,包含了三个独立的操作(读取-修改-写入)
- 由于不恰当的执行时序而出现不正确的结果(或者说计算结果的正确性取决于线程的交替执行顺序),这种情况称为竞态条件
- 一种典型的竞态条件——“先检查后执行”
首先观察到某个条件为真,然后根据这个条件做相应的动作,但事实上,在观察这个条件和做动作直接,观察的条件变为假了。这就会导致各种问题
- 举例:延迟初始化(将对象的初始化操作推迟到实际被使用时才进行,同时确保只被初始化一次)
@NotThreadSafe
//延迟初始化中的竞态条件
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
复合操作(Compound Actions)
- 我们将“读取-修改-写入”和“先检查后执行”称为复合操作:包含了一组必须以原子方式执行的操作以确保线程安全性
- java.util.concurrent.atomic包包含了原子变量类,比如通过使用线程安全类AtomicLong来代替long类型的计数器,就能确保对计数器状态的访问操作是原子的
- 在无状态的类中添加一个状态(注意是一个,多个状态又不是了)时,如果该状态完全由线程安全的对象来管理,那这个类仍然是安全的。所以要尽可能用现有的线程安全对象