Volatile原理解析

  • 原子性:一个操作或者多个操作,要么都被执行且不能被中断打断,要么都不执行;

    Java的原子性操作有以下三种:

    a. 基本类型的读取和赋值操作,且赋值必须是数字赋值给变量,变量之间的相互赋值不是原子性操作。

    b.所有引用reference的赋值操作

    c.java.concurrent.Atomic.* 包中所有类的一切操作

  • 可见性:当多线程访问同一个变量时,一个线程改变该变量,其他的线程也能立即看到修改的值;

  • 重排序:为了提高指令的运行性能,在编译时或者运行时对指令的执行顺序进行了调整,重排序又分为编译时重排序和运行时重排序。编译时重排序是指编译源代码的时候就对代码执行顺序进行分析,在遵循as-if-serial的原则前提下对源码的执行顺序进行调整,as-if-serial原则是指在单线程环境下,无论怎么重排序,代码的执行结果都是确定的。运行时重排序是指为了提高运行的速度,系统对执行的顺序进行调整。 

   一个线程修改共享变量的之后,另外一个线程真的能够马上拿到变量的最新值吗?我们来做一个测试:

	public class VolatileTest_1  {
	    private  static  boolean  flag=false;
	    public static void main(String[] args) throws InterruptedException {
	    Thread  thread_1=new Thread(new Runnable() {
	    @Override
	    public void run() {
	        while (true) {
	            if (flag) {
	             System.out.println(Thread.currentThread().getName()+":"+flag);
	            }
	        }
	    }
	    });
	    Thread thread_2=new Thread(new Runnable() {
	 
	 
	    @Override
	    public void run() {
	        flag=true;
	        System.out.println(Thread.currentThread().getName()+":"+flag);
	        }
	    });
	    thread_1.start();
	    Thread.sleep(1000);
	    thread_2.start();
	}
}

 运行结果:

可以看到线程0一直在循环,那么线程0并没有到线程1更新的flag值。


那么是什么原因导致了内存不可见了呢?

  1. cache缓存:因为cpu的速度远远高于主内存的速度,为减少资源的浪费,在cup上又设置了多级的cache,而线程运行的时候会先将数据拷贝到线程内部的cache里面,也就是工作内存(working memery),那么多个线程访问同一个变量是就变成每个线程访问自己的cache,变相导致了内存的不可见。

  2.重排序:除了cache的原因就是,执行顺序的重排序,导致线程A线程读取某个变量的时候,线程B还没将该变量的最新值写会主内存。

解决缓存一致性的操作有两种

  • 总线加锁:是一种独占式的方式,只能当前的cpu使用,其他的cpu进入阻塞的状态,效率低下;
  • 缓存一致性协议(MESI):确保缓存中使用的共享变量都是一致的

volatile的原理:有volatile修饰的共享变量进行写操作的候会多出Lock前缀的指令,该指令在多核处理器下会引发两件事情。

  1. 将当前处理器缓存行数据刷写到系统主内存。
  2. 这个刷写回主内存的操作会使其他CPU缓存的该共享变量内存地址的数据无效。

Volatile的使用场景:状态标记、double check

状态标记的应用场景:比如上面举的例子,用volatile修饰flag变量便能解决主存不可见的问题

Double checked:双重检查的单例模式

public class SingleTon_01 {
    private static  volatile   SingleTon_01  singleTon_01;
    private SingleTon_01(){}
    public static SingleTon_01 getSingleTon_01() {
        if (singleTon_01==null) {
            synchronized (SingleTon_01.class) {
                if (singleTon_01==null) {//避免其他的线程已对singleTon_01进行了赋值  
                    singleTon_01=new SingleTon_01();
                }
            }
        }
    return singleTon_01;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值