1.定义线程安全
当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的 —by Brian Goetz in Java Concurrency In Practice
文字是拗口点,毕竟是外包给中国人翻译的,不过从英文思维看,就好理解了。
类要成为线程安全的,首先必须在单线程环境中有正确的行为。如果一个类实现正确(这是说它符合规格说明的另一种方式),那么没有一种对这个类的对象的操作序列(读或者写公共字段以及调用公共方法)可以让对象处于无效状态,观察到对象处于无效状态、或者违反类的任何不可变量、前置条件或者后置条件的情况。
此外,一个类要成为线程安全的,在被多个线程访问时,不管运行时环境执行这些线程有什么样的时序安排或者交错,它必须仍然有如上所述的正确行为,并且在调用的代码中没有任何额外的同步。其效果就是,在所有线程看来,对于线程安全对象的操作是以固定的、全局一致的顺序发生的。
正确性与线程安全性之间的关系非常类似于在描述 ACID(原子性、一致性、独立性和持久性)事务时使用的一致性与独立性之间的关系:从特定线程的角度看,由不同线程所执行的对象操作是先后(虽然顺序不定)而不是并行执行的。
1.2 状态依赖
看代码。尽管 Vector的所有方法都是同步的,但是在多线程的环境中不做额外的同步,就使用这段代码仍然是不安全的。因为如果另一个线程恰好在错误的时间里删除了一个元素,则 get() 会抛出一个ArrayIndexOutOfBoundsException
调get()的先决条件是以 size()的结果要合法,即大于0,vector里要有元素才能get 。这种以一个方法的结果作为另一个方法的输入条件的模式,它就是一个状态依赖,在多线程情况下,必须保证至少在调用这两种方法期间,元素的状态没有改变。
private static Vector<Integer> vector = new Vector<>();
public static void main(String[] args) throws ClassNotFoundException {
while(true) {
for (int i = 0; i < 10; i++) {
vector.add(i);
}
Thread rm = new Thread(() -> {
for (int i =