搜索
您的当前位置:首页正文

线程安全问题之原子性、可见性、有序性

来源:吉趣旅游网

来源:https://www.bilibili.com/video/BV1Rv411y7MU?from=search&seid=9769842728262062596

0.三大特性

线程安全问题表现为三个方面:原子性、可见性、有序性

1. 原子性

原子(Atomic)表示不可分割. 原子操作的不可分割有两层含义:

1)访问(读、写)某个共享变量的操作从其他线程来看,该操作要么已经执行完毕,要么尚未发生,即其他线程得不到当前操作的中间结果(像CAS这种锁,就可以让多个线程同时操作资源,只是在提交的时候进行检查)

2)访问同一组实例变量是不能够交错的

Java实现原子性的两种方式

1)锁:锁具有排他性,保证共享变量某一时刻只能被一个线程访问

2)CAS指令:直接在硬件(处理器和内存)层次上实现,看作硬件锁

2. 可见性

在多线程环境中,一个线程对某个共享变量进行更新后,后续其他的线程可能无法立即读取到这个更新过后的结果,这就是线程安全的另一种问题:可见性(visibility)

如果一个线程对共享变量更新后,其他线程能够读取到更新后的结果,则称该线程对共享变量的更新对其他线程可见,否则为不可见.

多线程程序因为可见性问题可能导致其他线程读取到旧数据(脏数据).

3. 有序性

有序性(ordering)是指在什么情况下一个处理器上运行的一个线程所执行的内存访问操作在另一个处理器运行的线程来看是乱序的(Out of order)

乱序:内存访问的顺序看起来发生了变换

3.1 重排序

重排序:在多核环境下,编写的结构顺序,这种操作的执行顺序可能是没有保障的:

1)编译器可能会改变两个操作的先后顺序

2)处理器也可能不会按照目标代码的顺序执行

重排序是对内存访问操作的一种优化,可能在不影响单线程正确的情况下提升程序性能,但是对于多线程程序可能会影响正确性,导致线程不安全

重排序和可见性问题一样,不是必然会出现的.

3.2 指令重排序

在源码顺序与程序顺序不一致,或程序顺序与执行顺序不一致时,就说发生了指令重排序(Instruction Reorder)

指令重排序是一种动作,确实对指令的顺序做了调整,重排序的对象是指令

Javac编译器一般不执行指令重排序,而JIT编译器可能引起指令重排

处理器可能执行指令重排,使执行顺序与程序顺序不一致

指令重排不会对单线程程序产生影响

3.2 存储子系统重排序

由于CPU的执行速度很快,而主内存相对CPU的执行速度偏慢,那么在CPU中有一个高速缓存(cache)或者写缓冲器(write buffer)用于处理CPU与主内存处理速度差异较大的情况。其中将写缓冲器高速缓存统称为存储子系统

高速缓存是CPU用来匹配与主内存处理速度不一致而设计的

写缓冲器是用来提高写高速缓存的效率

即使处理器严格按照程序顺序来执行两个内存的访问操作,在存储子系统的作用下,其他处理器对这两个操作的感知顺序与程序顺序不一致,即这两个操作的顺序看起来像是发生了变化,这种现象称为存储子系统重排序.

存储子系统重排序并没有真正对指令顺序进行调整,而是发生了执行执行顺序被调整的假象。指令重排序操作的是指令,存储子系统重排序对象是内存操作的结果.

LoadLoad重排序,一个处理器先后执行的两个读操作L1和L2,其他处理器对两个内存操作的感知顺序可能是L2->L1

StoreStore重排序,一个处理器先后执行的两个读操作W1和W2,其他处理器对两个内存操作的感知顺序可能是W2->W1

LoadStore,一个处理器先后执行的L1和W1,其他处理器对两个内存操作的感知顺序可能是W1->L1

StoreLoad,一个处理器先后执行的W1和L1,其他处理器对两个内存操作的感知顺序可能是L1->W1

内存重排序与具体的处理器的架构有关,不同架构的处理器所允许的内存重排序可能不同

3.3 貌似串行语义

JIT编译器,处理器是按照一定的规则对指令或者内存操作的结果进行重排序.给单线程的程序造成一种假象----指令是按照源码的顺序执行的.这种现象称之为**貌似串行语义.**貌似串行语义不影响单线程程序的正确性,但是影响多线程程序.

为了保证貌似串行语义,在有数据依赖的语句不会被重排序,只有在不存在数据依赖关系的语句时才会被执行.如果两个操作(指令)访问同一个变量,且其中一个操作(指令)为写操作,那么这两个操作之间就存在数据依赖关系(Data Dependency)

如果不存在写操作,则可能进行重排序:

存在控制依赖的语句允许重排.一条语句(指令)的执行结果可能会影响到另一条指令(语句)能否被执行,这两条语句(指令)就存在依赖控制关系.如在if语句允许重排,那么可能存在处理器先处理if语句块再判断if条件的结果.

3.4 保证内存访问顺序

可以使用volatile或者synchronized关键字来保证有序性

因篇幅问题不能全部显示,请点此查看更多更全内容

Top