原子操作的特性
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子操作的特性相关的知识,希望对你有一定的参考价值。
参考技术A原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断。在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。但是,在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。我们以decl (递减指令)为例,这是一个典型的"读-改-写"过程,涉及两次内存访问。设想在不同CPU运行的两个进程都在递减某个计数值,可能发生的情况是:
⒈ CPU A(CPU A上所运行的进程,以下同)从内存单元把当前计数值⑵装载进它的寄存器中;
⒉ CPU B从内存单元把当前计数值⑵装载进它的寄存器中。
⒊ CPU A在它的寄存器中将计数值递减为1;
⒋ CPU B在它的寄存器中将计数值递减为1;
⒌ CPU A把修改后的计数值⑴写回内存单元。
⒍ CPU B把修改后的计数值⑴写回内存单元。
我们看到,内存里的计数值应该是0,然而它却是1。如果该计数值是一个共享资源的引用计数,每个进程都在递减后把该值与0进行比较,从而确定是否需要释放该共享资源。这时,两个进程都去掉了对该共享资源的引用,但没有一个进程能够释放它--两个进程都推断出:计数值是1,共享资源仍然在被使用。
JMM与并发三大特性
1、JMM与原子性
Java中对基本数据类型变量的读取赋值都是原子性的,对引用类型变量的读取和赋值也是原子性。这类操作都是不可被中断的,要么执行,要么不执行。
(1) 赋值操作:x = 10;
操作是原子性。
(2) 赋值操作:y = x;
操作是非原子性。将变量x赋值给y,包含两个重要步骤:
1)执行线程从主存中读取x的值,然后将其存入当前线程的工作内存
2)在执行线程的工作内存中修改y的值为x,然后将其写入主存
两个步骤都是原子类型操作,但组合在一起就不是了
(3) 自增操作:y++;
操作是非原子性:它包含了三个重要步骤:
1)执行线程从主存中读取y的值,将其存入当前线程的工作内存
2)在执行线程工作内存中为y执行加1操作
3)将y值写回主存
- 多个原子性操作合在一起就不再是原子性操作
- 简单的读取与赋值操作是原子性操作,但将一个变量赋值给另一个变量的操作是非原子性操作
- JMM只保证基本读取和赋值的原子性操作,其他的均不保证
2、JMM与可见性
多线程环境下,某线程首次读取共享变量,首先到主存中获取,存入工作内存。如果对该变量执行修改操作,则将新值写入工作内存,再刷新到主存。但是这一刷新的时间是不确定的,其他线程不知道该线程的修改操作,因此Java提供三种方式保障可见性:
- 使用volatile关键字,该关键字实现基础为MESI缓存一致性协议
- 通过synchornized关键字
- 通过JUC提供的显示锁
3、JMM与有序性
Java内存模型中,允许编译器和处理器对指令进行重排序,
多线程下重排序会影响程序的正确运行,Java提供volatile、synchornized和显示锁三种方式保证有序性。
3.1 happens-before原则
除三种方式之外,JMM本身存在有序性规则,这个规则不需要任何同步手段就能保证有序性,这个规则称为happens-before原则。若两个操作不满足happens-before原则以及由它推导出的规则,那么这两个操作就没有顺序保障,JVM或处理器可以对它们任意排序:
- 程序次序规则:在一个线程内,按照代码次序执行,编写在后面的操作发生于编写在前面的操作之后。
这一条看似是说程序按照次序执行,但虚拟机、处理器还是会对指令进行重排序,只能保障程序执行的结果与顺序执行的结果一致。多线程下无法保障正确性。
- 锁定规则:一个unLock操作要先行发生于后面对同一个锁的lock操作。
这条规则意思是,无论单线程还是多线程环境下,一个锁出于被锁定状态,那么必须先释放锁再加锁。
- volatile变量规则:对一个变量的写操作要先行发生于后面对这个变量的读操作。
这条规则意思是,如果一个变量是volatile修饰的,一个线程对它进行读操作,一个线程对它进行写操作,那么写入操作肯定要先行发生于读操作。这条规则标志了线程间的可见性。
- 传递规则:如果操作A先于操作B,而B由先于操作C。则肯定可以得到A先于C。
这条规则体现了happens-before原则具有传递性。
- 线程启动规则:Thread对象的start( )方法先行发生于对该线程的任何动作。
只有start( )之后线程才真正运行,否则Thread也只是一个对象。
- 线程中断规则:对interrupt( )方法的调用先行发生于被中断线程捕获到中断信号。
这条规则是指,如果线程收到中断信号,那么在此之前势必调用了interrupt( )方法。
- 线程终结规则:线程中所有的操作都要先行发生于线程的终止检测。
这条规则意思是,线程任务的执行、逻辑单元执行肯定要先发生于线程死亡之前。
- 对象的终结规则:一个对象初始化的完成先行发生于finalize( )方法之前。即先生后死。
以上是关于原子操作的特性的主要内容,如果未能解决你的问题,请参考以下文章