正确理解volatile关键字

volatile3个特点:

1.强制刷新主内存

2.不保证原子性

3.防止指令重排序


这3点之前我也学习过,我也认为自己搞懂了,但是在看了JVM后我对其原理产生了质疑,最后终于理清楚了,特此记录。


 1 public class RunThread extends Thread {
 2 
 3     private boolean isRunning = true;
 4 
 5     public boolean isRunning() {
 6         return isRunning;
 7     }
 8 
 9     public void setRunning(boolean isRunning) {
10         this.isRunning = isRunning;
11     }
12 
13     @Override
14     public void run() {
15         System.out.println("进入到run方法中了");
16         while (isRunning == true) {
17         }
18         System.out.println("线程执行完成了");
19     }
20 }
21 
22 public class Run {
23     public static void main(String[] args) {
24         try {
25             RunThread thread = new RunThread();
26             thread.start();
27             Thread.sleep(1000);
28             thread.setRunning(false);
29         } catch (InterruptedException e) {
30             e.printStackTrace();
31         }
32     }
33 }

还是这个例子,while的条件中,isRunning每次取的都是拷贝副本

如果你加以volatile关键字,他的查询,就会转移到主内存进行一个查询

所以volatile在这里的作用是:禁止“读取”相关的一系列字节码操作,是从本地内存读的。


也就是说,这一串字节码操作,你都得重新走一遍。原来的是读进来作为拷贝变量以后,还有可能读取这个拷贝变量。用了volatile关键字以后,就禁止了读取拷贝变量。


对于不能防止原子自增:


这是普通变量的一个情况

如果加了volatile关键字,你对于该变量的操作,另外的所有线程都能感知到。也就是说这个变量变成了实时数据。但是问题在于,虽然这很好的做到了,你在读取这个变量的时候,可以确保他是实时的。但是i=i+1这个操作,你读取i,确实是实时的, 但是i+1后不会立刻进行一个赋值,也就是说,你使得i+1了,但是你没有进行一个赋值操作,其他线程又何来感知i的值的变化呢?这就是问题最关键的原因,i+1和赋值之间存在一定的时间差。假设这样一个场景:10个线程同时加了1,虽然是多线程,但是i的赋值总有一个先后,哪怕你赋值成功了,其他线程也无从得知,因为问题已经变成了,10个值同时去更新i。他不在乎你i之前是多少。这样讲有点极端,但是事实就是如此。


AutoInteger是如何做到原子自增的呢?

这里就是用到了CAS,乐观锁,当若干线程更新一个值的时候,同时只有一个线程可以更新,而其他线程就会更新失败。更新失败怎么办呢?他们就会一直等待,重新开始,直到成功为止。

那么CAS代码是如何实现的?

public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

这个compareAndSwapInt你可以认为他就是CAS了,他是native方法。我们是通过它来实现原子自增的。


最后,指令重排序,设定内存屏障:

这个我了解不深,目前也不想深入,简单举个知乎的例子https://www.zhihu.com/question/40885211?sort=created

//x、y为非volatile变量
//flag为volatile变量
x = 2; //语句1
y = 0; //语句2
flag = true; //语句3
x = 4; //语句4
y = -1; //语句5

由于3是volatile,而且是写,所以编译器在voaltile写操作之前插入一个release barrier(相当于loadstore+storestore)实现。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值