线程安全性
什么是线程安全性
定义线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
在线程安全类中封装了必要的同步机制,因此客户端无须进行进一步采取同步措施。
无状态的对象一定是线程安全的。
原子性
竞态条件
在并发编程中,这种由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,它有个正式的名字:竞态条件(Race Condition)
Servlet that counts requests without the necessary synchronization.
|
|
Race condition in lazy initialization.
|
|
Servlet that counts requests using AtomicLong.
|
|
当无状态的类中添加一个状态时,如果该状态完全友线程安全的对象来管理,那么这个类任然是线程安全的。
在实际情况中,应尽可能使用现有线程安全的对象来管理类的状态。与非线程安全的对象相比,判断线程安全对象的可能状态及其状态转换情况要更为容易,从而也更容易维护和验证线程的安全性。
加锁机制
Servlet that attempts to cache its last result without adequate atomicity.
|
|
要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。
内置锁
同步方法 (Synchronized Method)
同步代码块(Synchronized Block)
Servlet that caches last result, but with unnacceptably poor concurrency.
|
|
重入
活跃性与性能
Servlet that caches its last request and result.
对在单个变量上实现原子操作来说,原子变量是很有用的,但是由于我们已经使用了同步代码块来构造原子操作,而使用两种不同的同步机制不仅会带来混乱,也不会在性能或安全性上带来任何好处,因此在这里不使用原子变量。
重构后的CachedFactorizer实现了在简单性(对整个方法进行同步)与并发性(对尽可能段的代码路径进行同步)之间的平衡。
要判断同步代码块的合理大小,需要在各种设计需求之间进行权衡,包括安全性(这个需求必须得到满足)、简单性和性能。有时候在简单性和性能之间会发生冲突,但在二者之间通常能找到某种合理的平衡。
通常,在简单性和性能之间存在着相互制约因素。在实现某个同步策略时,一定不要盲目地为了性能而牺牲简单性(这可能为破坏安全性)。