volatile的理解

介绍一下和volatile相关的内存模型概念

%

  1. 内存的读写速度比CPU执行指令的速度慢很多
  2. 程序运行过程中,需要先从主存复制到CPU的高速缓存中,运算结束后,再刷新到主存中
  3. 单线程执行i=0, i=i+1不会出错,多线程,可能造成i最后还是1。
  4. 硬件上有2种解决方式:上锁和缓存一致性协议(volatile)
  5. 缓存一致性:其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

介绍一下并发编程常见的问题

%

要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

  1. 原子性:一个操作或多个操作,要么全部执行且过程不会被打断,要么就都不执行。比如A给B转钱,必然有2个步骤:A-1000, B+1000,中间突然中止了,B就收不到了。
  2. 可见性:多个线程访问同一变量的时候,一个线程修改了这个变量,其他线程可见。比如线程2修改了a,线程1读入的还是a之前的值。
  3. 有序性:程序执行的顺序按照代码的先后顺序来执行。一般来说,处理器为了提高程序运行效率,会对指令进行重排,单线程没问题。1111

判断下面代码哪些是原子性操作?

x = 10;         //语句1
y = x;         //语句2
x++;           //语句3
x = x + 1;     //语句4

%

只有语句1是原子性的。语句2涉及x的读入。


Java如何保证可见性

%

  1. volatile关键字来保证可见性,当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
  2. 通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

Java如何保证有序性

%

  1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
  2. 锁定规则:无论在单线程中还是多线程中,同一个锁如果出于被锁定的状态,那么必须先对锁进行了释放操作,后面才能继续进行lock操作。
  3. volatile变量规则:如果一个线程先去写一个变量,然后一个线程去进行读取,那么写入操作肯定会先行发生于读操作。

volatile的作用

%

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

2)禁止进行指令重排序。


volatile能保证原子性吗

%

不能,如果线程1读入某个变量,中断,线程2在修改这个变量,线程1再修改这个变量是在自己cpu缓存里执行的,不会重新读入。


volialte的使用场景举例

%

  1. 状态标记量
volatile boolean inited = false;
//线程1:
context = loadContext();  
inited = true;            
 
//线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);

double check的单例模式为什么需要voliatle

%

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(uniqueInstance == null){
        // B线程检测到uniqueInstance不为空
            synchronized(Singleton.class){
                if(uniqueInstance == null){
                    uniqueInstance = new Singleton();
                    // A线程被指令重排了,刚好先赋值了;但还没执行完构造函数。
                }
            }
        }
        return uniqueInstance;// 后面B线程执行时将引发:对象尚未初始化错误。
    }
}

具体来说就是synchronized虽然保证了原子性,但却没有保证指令重排序的正确性,会出现A线程执行初始化,但可能因为构造函数里面的操作太多了,所以A线程的uniqueInstance实例还没有造出来,但已经被赋值了。而B线程这时过来了,错以为uniqueInstance已经被实例化出来,一用才发现uniqueInstance尚未被初始化。要知道我们的线程虽然可以保证原子性,但程序可能是在多核CPU上执行。


参考

%

  1. https://www.cnblogs.com/dolphin0520/p/3920373.html

  2. https://blog.csdn.net/u011248395/article/details/71087379?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值