4.3 原子性、可见性与有序性

2016-06-10 14:29:21 6,076 0

Java内存模型是围绕着并发过程中如何处理原子性可见性有序性这三个特征来建立的。

原子性(Atomicity)

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

上面一句话虽然看起来简单,但是理解起来并不是那么容易。看下面一个例子:

请分析以下哪些操作是原子性操作:


1  x = 10; //语句1


2  y = x; //语句2


3  x++; //语句3


4  x = x + 1; //语句4

乍一看,有些朋友可能会说上面的4个语句中的操作都是原子性操作。其实只有语句1是原子性操作,其他三个语句都不是原子性操作。

语句1是直接将数值10赋值给x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中。

语句2实际上包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。

同样的,x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。

所以上面4个语句只有语句1的操作具备原子性。也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。

需要注意的是,如有一段代码中有多个原子操作,但是不同的原子操作整合在一起,这段代码可能就不是原子操作了。

在java中,我们可以通过目前已经看到的synchronized关键字和在之后章节中将会介绍到Lock对象,来保证语句执行的原子性。

可见性(Visibility)

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

在Java中,synchronized关键字、Lock对象和我们后面将介绍到的volatile关键字都可以实现共享变量的可见性。关于可见性的问题,这涉及到硬件的内存模型。我们后面将会有一节专门介绍volatile关键字,到时我们会对这个概念进行更加详细的讲解。

有序性(Ordering)

在Java中,一个线程中的代码总是串行执行的。如果同时存在多个线程,在没有采取同步措施的情况下,我们是无法保证线程的先后执行顺序的,线程可以并行的执行。反之,如果我们采取了同步措施,例如多个线程竞争同一个锁,那么只有获取到锁的线程才能执行,也就是说,这种情况下,即使存在多个线程,也不同同时执行,必须是一个线程执行完成后,下一个抢到锁的线程才能执行...

在Java中,synchronized关键字、Lock对象以及volatile关键字都可以保证多个线程执行时的有序性。


总结

synchronized关键字、Lock对象同时支持原子性、有序性和可见性 

volatile关键字只支持可见性和有序性,并不保证原子性。