原子性:即一个操做或者多个操做 要么所有执行而且执行的过程不会被任何因素打断,要么就都不执行。多线程
在Java中,对基本数据类型的变量的读取和赋值操做是原子性操做,即这些操做是不可被中断的,要么执行,要么不执行。并发
上面一句话虽然看起来简单,可是理解起来并非那么容易。看下面一个例子:spa
请分析如下哪些操做是原子性操做:线程
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的操做具有原子性。
也就是说,只有简单的读取、赋值(并且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操做)才是原子操做。
不过这里有一点须要注意:在32位平台下,对64位数据的读取和赋值是须要经过两个操做来完成的,不能保证其原子性。可是好像在最新的JDK中,JVM已经保证对64位数据的读取和赋值也是原子性操做了。
从上面能够看出,Java内存模型只保证了基本读取和赋值是原子性操做,若是要实现更大范围操做的原子性,能够经过synchronized和Lock来实现。因为synchronized和Lock可以保证任一时刻只有一个线程执行该代码块,那么天然就不存在原子性问题了,从而保证了原子性。
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其余线程可以当即看获得修改的值。
于可见性,Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会当即被更新到主存,当有其余线程须要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,由于普通共享变量被修改以后,何时被写入主存是不肯定的,当其余线程去读取时,此时内存中可能仍是原来的旧值,所以没法保证可见性。
另外,经过synchronized和Lock也可以保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁而后执行同步代码,而且在释放锁以前会将对变量的修改刷新到主存当中。所以能够保证可见性。
有序性:即程序执行的顺序按照代码的前后顺序执行。
在Java内存模型中,容许编译器和处理器对指令进行重排序,可是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
在Java里面,能够经过volatile关键字来保证必定的“有序性”(具体原理在下一节讲述)。另外能够经过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每一个时刻是有一个线程执行同步代码,至关因而让线程顺序执行同步代码,天然就保证了有序性。