1. 竞态
对于同样的输入,程序的输出有时候正确而有时候却是错误的。这种一个计算结果的正确性与时间有关的现象就被称为竞态(RaceCondition)
导致竞态的常见原因是多个线程在没有采取任何措施的情况下并发更新、读取同一个共享变量。
竞态往往伴随着数据的脏读问题,即线程读取到一个过时的数据;丢失更新问题,即一个线程丢失数据所做的更新没有体现在后续其他线程对该数据的读取上。
不同线程各自访问各自的那一部分局部变量(包括形式参数和方法体内定义的变量),所以局部变量不会导致竞态。
线程安全 如果一个类在单线程环境下运行正常,并且在多线程环境下,不做任何改变的情况下也能正常运行,那我们就称其是线程安全的,相应的我们称这个类具有线程安全性。
非线程安全 反之我们则为非线程安全。
2. 线程安全问题
线程安全问题概括来说表现为3个方面: 原子性(atomic),可见性(visibility)和有序性(ordering)。
线程终止与可见性
public class ThreadJoinVisibility {
static int data = 0;
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run(){
data = 1;
}
};
thread.start();
try {
/**
* java语言规范保证一个线程终止后该线程对共享变量的更新对
* 于调用该线程的join方法的线程而言是可见的。
*/
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(data);
}
}
3. 编译器可能改变两个操作的先后顺序;处理器可能不是完全依照程序的目标代码所指定的顺序执行指令;另外,一个处理器上执行的多个操作,从其他处理器的角度来看其顺序可能与目标代码所指定的顺序不一致。这种现象就叫做重排序(Reordering).
4. 指令重排序
从底层的角度来说,禁止重排序是通过调用处理器提供相应的指令(内存屏障 memory barrier)来实现的。 volatile关键字, synchronized关键字都能够实现有序性。
5. 上下文切换(context switch)
在某种程度上,可以被看做多个线程共享同一个处理器的产物,它是多线程编程中的一个重要概念。
6. 一次只能够被一个线程占用的资源被称为排他性(Exclusive)资源. 常见的排他性资源包括处理器,数据库连接,文件等。
7. 同一时间内,处于运行状态(即生命周期状态为RUNNABLE的RUNNING子状态的线程)的线程数量越多,我们就称并发的程度越高,简称高并发。