您的位置 首页 java

Java并发编程(六)三大特性:原子性、可见性、有序性

上一篇:

一、原子性

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

  一个很经典的例子就是银行账户转账问题:

  比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。

  试想一下,如果这2个操作不具备原子性,会造成什么样的后果。假如从账户A减去1000元之后,操作突然中止。然后又从B取出了500元,取出500元之后,再执行 往账户B加上1000元 的操作。这样就会导致账户A虽然减去了1000元,但是账户B没有收到这个转过来的1000元。

  所以这2个操作必须要具备原子性才能保证不出现一些意外的问题。

  同样地反映到并发编程中会出现什么结果呢?

  举个最简单的例子,大家想一下假如为一个32位的变量赋值过程不具备原子性的话,会发生什么后果?

   i = 9;

  假若一个 线程 执行到这个语句时,我暂且假设为一个32位的变量赋值包括两个过程:为低16位赋值,为高16位赋值。

  那么就可能发生一种情况:当将低16位数值写入之后,突然被中断,而此时又有一个线程去读取i的值,那么读取到的就是错误的数据。

二、可见性

可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改;

如:线程1和线程2同时加载变量 flag=1到自己的本地内存中来,但是2个线程在不同的cpu运行此时可能就会出现线程1将flag修改为2,但是这个时候只是在自己的工作内存中,或者说cpu的高速缓存中,还没来得及写入主内存,也有可能写入了主内存,但是线程2没有及时去刷新主内存的数据,这个时候就会导致线程2读取的flag还是为1;

这个就是并发编程中比较常见的可见性问题,后续会讲解 volatile 如何解决可见性问题的;

三、有序性

编译器和指令器,有的时候为了提高代码执行效率,会将指令重排序;

 public class Singleton {
     private  Singleton() { }
    private volatile  static  Singleton instance;
    public Singleton  getInstance (){
        if(instance==null){
             synchronized  (Singleton.class){
                if(instance==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  

这条语句实际上包含了三个操作:1.分配对象的内存空间;2.初始化对象;3.设置instance指向刚分配的内存地址。但由于存在重排序的问题,可能有以下的执行顺序:

如果2和3进行了重排序的话,线程B进行判断if(instance==null)时就会为true,而实际上这个instance并没有初始化成功,显而易见对线程B来说之后的操作就会是错得。而用volatile修饰的话就可以禁止2和3操作重排序,从而避免这种情况。volatile包含禁止指令重排序的语义,其具有有序性。

文章来源:智云一二三科技

文章标题:Java并发编程(六)三大特性:原子性、可见性、有序性

文章地址:https://www.zhihuclub.com/170987.shtml

关于作者: 智云科技

热门文章

网站地图