可见性:一个线程对共享变量的修改,更够及时的被其余线程看到安全
原子性:即不可再分了,不能分为多步操做。好比赋值或者return。好比"a = 1;"和 "return a;"这样的操做都具备原子性。相似"a += b"这样的操做不具备原子性,在某些JVM中"a += b"可能要通过这样三个步骤:多线程
1. 取出a和b
2. 计算a+b
3. 将计算结果写入内存
复制代码
Synchronized可以实现原子性和可见性;在Java内存模型中,synchronized规定,线程在加锁(对象锁,类锁)时的工做步骤:函数
PS:优化
当synchronized修饰一个static方法时,多线程下,获取的是类锁(即Class自己,注意:不是实例);spa
当synchronized修饰一个非static方法时,多线程下,获取的是对象锁(即类的实例对象)线程
Volatile实现内存可见性是经过内存屏障store和load指令完成的;code
对volatile变量执行写操做时,会在写操做后加入一条store指令,即强迫线程将最新的值刷新到主内存中;对象
而在读操做时,会加入一条load指令,即强迫从主内存中读入变量的值。但volatile不保证volatile变量的原子性,例如:排序
Private int Num=0;
Num++;//Num不是原子操做
复制代码
Num++不是原子操做,由于其能够分为:读取Num的值,将Num的值+1,写入最新的Num的值。内存
对于Num++;操做,线程1和线程2都执行一次,最后输出Num的值多是:1或者2
【解释】
输出结果1的解释:当线程1执行Num++;语句时,先是读入Num的值为0,假若此时让出CPU执行权,线程得到执行,线程2会从新从主内存中,读入Num的值仍是0,而后线程2执行+1操做,最后把Num=1刷新到主内存中;线程2执行完后,线程1由开始执行,但以前已经读取的Num的值0,因此它仍是在0的基础上执行+1操做,也就是仍是等于1,并刷新到主内存中。因此最终的结果是1.通常在多线程中使用volatile变量,为了安全,对变量的写入操做不能依赖当前变量的值:如Num++或者Num=Num*5这些操做。
在双重检查单例模式下使用 Volatile 来保证线程安全的原理就是利用了 Volatile 修饰的变量由于插入了内存屏障,禁止了指令重排,从而保证了有序性.即 User user = new User()这个操做,本来的是会被分解为三个操做:
(a)给Singleton的实例分配内存
(b)调用Singleton()的构造函数,初始化成员字段;
(c)将singleton对象指向分配的内存空间(即singleton不为空了);
复制代码
变量加上volatile能够防止对象初始化的无序性,对象必须初始化完成后才将地址返回。