原子性,有序性和可见性

120 阅读4分钟

学习多线程操作三大性质:原子性,有序性和可见性

    上一篇synchronized    ←||→    下一篇

原子性

原子性是指一个操作是要么全部执行成功要么全部执行失败。即便在多线程环境下,一个操作一旦开始,就不会被其他线程所干扰。

 int a = 10;
 a++;
 int b = a;

以上操作只有第一句是原子性的。

a++可以拆分为 ①读取a ②a加一③将a加一结果给a

int b = a;可拆为: ①读取a②将a赋给b

jMM定义的8个原子操作:

  1. lock(锁定):作用于主内存中的变量,它把一个变量标识为一个线程独占的状态;
  2. unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  3. read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便后面的load动作使用;
  4. load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中的变量副本
  5. use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;
  6. assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
  7. store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后的write操作使用;
  8. write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

把一个变量从主内存中复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行store,write操作。

注意的是:java内存模型只是要求上述两个操作是顺序执行的并不是连续执行的。也就是说read和load之间可以插入其他指令,store和writer可以插入其他指令。

其中六条指令对于基本数据类型的读写具备原子性。也就是int a = 10; 的store,write两个指令之间不会插入其他指令。


synchronized原子性

对于lock和unlock这种底层指令jvm提供了monitorenter和monitorexit操作,对应于synchronized也就是synchronized具备原子性。


volatile不具备原子性

下面的例子结果总是小于10000的,因为volatile的作用是在某个线程改变volatile变量时会通知其他线程工作空间中的volatile变量无效。如果在这之前其他线程已经读取了工作空间的旧值就不会再取读取工作空间中更新的值,因此结果必然小于10000。

也就是volatile不具备原子性。

 class volatileDemo{
     private volatile static int a = 0;
     public static void main(String[] args) throws InterruptedException {
         for (int i = 0; i < 10; i++) {
             new Thread(()->{
                 for (int i1 = 0; i1 < 1000; i1++) {
                     a++;
                 }
             }).start();
         }
         Thread.sleep(2000);
         System.out.println(a);
     }
 }

有序性

synchronized

synchronized是阻塞同步,及该操作是互斥的,也就是在同一时刻只能由一个线程获取锁。因此,表现为排队执行,自然synchronized具有有序性。

volatile

在java内存模型中说过,为了性能优化,编译器和处理器会进行指令重排序;也就是说java程序天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。这里的意思是在单线程内程序执行的逻辑是有序的,并且jmm提供的happends-before规则保证了单线程内的内存可见性,也就保证了程序执行的结果符合预期。而在多线程的情况下由于指令重排及线程交替执行,会出现程序执行结果不符合预期。

而vlotile就是为了禁止被volatile修饰的变量的指令重排序。

具有有序性。


可见性

可见性是指当前线程修改临界区变量后,其他线程可以感知修改并可以获取最新的值。

synchronized通过加锁之前读取主内存最新的值,释放锁将工作内存修改的数据写到主内存中。从而synchronized具备可见性。

volatilo,在修改volatilo变量时通过加入内存屏障,将工作内存中的数据强行写入主内存,并且通知其他线程所拥有的该变量无效。在感知所在工作内存数据无效后会去主内存读取最新的数据。从而保证可见性。


总结

synchronized: 具有原子性,有序性和可见性 volatile:具有有序性和可见性