Java多线程的开发中有三种特性:原子性、可见性和有序性

       在java开发中高并发,多线程其实是一个问题。需要解决的也是一个问题,那就是内存共享问题。这个如果我们知道JVM的内存模型,这个问题其实很简单,也很基础。本文不谈内存模型,我们直观的进入高并发,多线程所需要面对的开发问题:原子性、可见性和有序性问题。

原子性(Atomicity)
原子性描述的是多线程执行的问题。
原子性是指在一个操作中在cpu一个调度中是连续不可中断的,要么执行成功,要么执行失败。纵观一下几乎说有的原子性说的都是同一个理论。分布式数据库原子性,分布式事务原子性,数据库原子性。大家都一样,都是保障在一个最小执行单元内,操作连续不可中断,要么成功要么失败 。不同的地方就在与最小执行单元的范围不同,对于在JVM环境中执行的多线程场景,这样的最小执行单元就是CPU的一次调度。

可见性(Visibility)
可见性描述的是多线程共享变量的问题。
可见性是指线程对共享变量只能在主存区进行交换,只能在工作内存区进行操作。说起来有的笼统,这个涉及jvm内存模型。jvm内存模型规定。jvm内存分为主存区和工作内存区;任何线程只能操作工作内存区的变量,不能操作主存区。所有共享变量需要线程在从主存区加载到工作内存才能读取,从工作内存刷新到主存区才能被其他线程共享。

有序性
程序执行的顺序按照代码的先后顺序执行就叫做有序性。可以使用volatile关键字来防止指令重排行(只在heppen-befor内存模型中存在指令重排行)heppen-before八大原则是保障有序性的重要依据。
单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。
volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作(当然也包括写操作了)。
happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。
线程中断的happen-before原则 :对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。
对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。


单例模式的双重锁为什么要加volatile
其实在这个问题首先要确定JVM的内存模型是否是heppen-before(指令重排行),

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


这就是单例模式的双重锁实现,为什么这里要加volatile关键字呢?我们把instance = new Singleton();这行代码进行拆分。可以分解为3个步骤:

(1)memory=allocate();// 分配内存

(2)ctorInstanc(memory) //初始化对象

(3)instance=memory //设置s指向刚分配的地址

如果没有volatile关键字,可能会发生指令重排序。在编译器运行时,从1-2-3 排序为1-3-2。此时两个线程同时进来的时候出现可见性问题,也就是说一个线程执行了1-3,另外一个线程一进来直接返回还未执行2的null对象。而我们的volatile关键之前已经说过了,可以很好地防止指令重排序。也就不会出现这个问题了。

如果我们学过java并发系列的其他类比如说Atomic等,通过源码我们会发现volatile无处不在。

你可能感兴趣的:(Java基础)