安全:安全的首先是正确的且是我们预期的,
正确性:某个类的行为与其规范完全一致。
在良好的规范中通常会定义各种不变性条件来约束对象的状态,以及定义各种后验条件来描述对象操作的结果。我们根据这些规范在单线程中运行获取正确的预期结果,代表这个程序的正确性,即所见即所知。
而当多个线程同时访问某个类,这个类始终能保持其正确性,我们就认为这个类是线程安全的。
(无状态对象一定是线程安全的)
原子性
原子性指操作不被中断,直到结束。
竞态条件
某个计算的正确性取决于多个线程的交替执行时序,就发生了竞态条件(是否正确看运气)。
1、先检查后执行
例如A和B相约星巴克,A到了之后看B不在,走了,刚走B来了。这个流程就发生了检查与执行不一致。
这种观察结果的失效就是大多数竞态条件的本质-基于一种可能失效的观察结果,做出判断或执行某个操作。
a、延迟初始化
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
A与B同时初始化LazyInitRace,A、B同时getInance操作,A已经在创建对象返回前,而B刚判断对象为空准备执行,则结果是A、B分别得到不同的对象。而getInstance预期的是得到一个相同的对象。
要避免竞态的问题,就是要让检查执行成为一个原子操作。
使用锁来保护状态
访问共享状态的复合操作,都必须是原子的,在执行复合操作的时候持有一个锁,可是复合操作成为一个原子操作。
用同步来协调对某个变量的访问,那么在访问该变量所在的位置上所有操作这个变量都需要同步。
每个共享的和可变的变量都应该只由同一个锁保护。
锁不可滥用,且尽管将所有方法均使用锁,也不能保证程序的安全性
if(!vector.contains(element))
vector.add(element);
vector.contains与vector.add都是原子操作,而上面只能称之为复合操作。