欢迎来到代码驿站!

JAVA代码

当前位置:首页 > 软件编程 > JAVA代码

java 中的volatile关键字

时间:2022-03-08 10:18:05|栏目:JAVA代码|点击:

1.volatile实现可见性的原理是什么?

volatile变量修饰的共享变量进行写操作的时候汇编代码会多出一个Lock前缀指令。

在该指令下,多核处理器会引发两件事:

  • 将当前处理器缓存行的数据写回系统内存
  • 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效

这里需要简单了解CPU缓存一致性问题:多核处理器环境下,每个CPU都有自己的缓存行,缓存了内存中的数据,要维护多个CPU中缓存的数据一致性,就需要解决两个问题:

  • 一是写传播(某个CPU里的cache数据更新时,需要传播到其他CPU的cache中);
  • 二是事务的串行化执行(在某个CPU里对数据的修改,在其他CPU中看起来顺序是一样的,也就是要引入近似[锁]的概念,保证同一时刻只有一个CPU可以对数据做修改);

写传播是通过[总线嗅探]完成的:通过总线把修改数据的事件广播通知给其他所有的核心,每个CPU核心都会监听总线上的广播事件,并检查是否有相同的数据在自己的Cache里面;而事务的串行化则通过[MESI协议]来完成。

MESI(Modified(已修改)、Exclusive(独占)、Shared(共享)、Ivalidated(已失效))协议中,如果要修改一个共享数据,不能直接修改,要先向其他CPU广播一个请求,把其他CPU cache中对应的数据状态改为Invalidated;以后其他CPU在读取标记为Invalidated的数据时,需要强制从内存中读取数据。

2.演示volatile的可见性

public class VolatileDemo {
    static  int flag = 1;  // 定义一个共享变量
    public static void main(String[] args) {
        // 两个线程,一个线程负责读取flag的值,另一个线程负责修改flag的值
        new Thread(){
            int localflag = flag;
            @Override
            public void run() {
                while(true){
                    //flag被修改后就跟localflag不一样了
                    if(localflag!=flag){
                        System.out.println("读到了flag修改后的值:"+ flag);
                        //把读到的值赋值给本地变量
                        localflag = flag;
                    }
                }
            }
        }.start();

        new Thread(){
            int localflag = flag;
            @Override
            public void run() {
                while (true){
                    //一直对flag的值进行修改
                    System.out.println("对flag的值进行修改:"+ ++localflag);
                    flag = localflag;
                    //休眠一秒更好地观察结果
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();


    }
}

可以看到另一个线程并不能及时读取到被修改的值。

共享变量用volatile修饰后:

public class VolatileDemo {
    static  volatile int flag = 1;
    public static void main(String[] args) {
        // 两个线程,一个线程负责读取flag的值,另一个线程负责修改flag的值
        new Thread(){
            int localflag = flag;
            @Override
            public void run() {
                while(true){
                    //flag被修改后就跟localflag不一样了
                    if(localflag!=flag){
                        System.out.println("读到了flag修改后的值:"+ flag);
                        //把读到的值赋值给本地变量
                        localflag = flag;
                    }
                }
            }
        }.start();

        new Thread(){
            int localflag = flag;
            @Override
            public void run() {
                while (true){
                    //一直对flag的值进行修改
                    System.out.println("对flag的值进行修改:"+ ++localflag);
                    flag = localflag;
                    //休眠一秒更好地观察结果
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();


    }
}

可以看到用volatile修饰后,每次另一个线程总能读取到修改后的值。

上一篇:SpringBoot 如何根据不同profile选择不同配置

栏    目:JAVA代码

下一篇:详谈Spring框架之事务管理

本文标题:java 中的volatile关键字

本文地址:http://www.codeinn.net/misctech/195608.html

推荐教程

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:914707363 | 邮箱:codeinn#126.com(#换成@)

Copyright © 2020 代码驿站 版权所有