原子操作的特性

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( )方法之前。即先生后死。

以上是关于原子操作的特性的主要内容,如果未能解决你的问题,请参考以下文章

MySQL8.0新特性——支持原子DDL语句

MySQL8.0新特性——支持原子DDL语句

JMM与并发三大特性

JMM与并发三大特性

事务的特性

解密并发幕后黑手:线程切换引发的原子性问题