JMM与并发三大特性

Posted privatenotesofaidanchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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( )方法之前。即先生后死。

以上是关于JMM与并发三大特性的主要内容,如果未能解决你的问题,请参考以下文章

JMM以及并发三大特性介绍(包括解决方案)

并发基础并发的三大特性

并发基础并发的三大特性

Java并发多线程编程——volatile关键字

学妹教你并发编程的三大特性:原子性可见性有序性

多线程&高并发深入理解JMM产生的三大问题原子性可见性有序性