• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:JAVA并发编程Volatile关键字

java 搞代码 4年前 (2022-02-19) 27次浏览 已收录 0个评论

1.Volatile关键字是什么?

2.Volatile关键字的作用

2.1 JMM(JAVA内存模型)的了解

2.2 Volatile的保障可见性

2.3 Volatile禁止指令重排序

3.用volitile改写单例模式

1.Volatile关键字是什么?
咱们都晓得synchronize关键字,是哟中分量型的同步机制,相对而言,volitile是一种轻量级的同步机制,volatile字面有“易挥发”的意思,Volatile关键字用于润饰共享可变变量,因为被润饰的值容易变动(容易被其它线程更改),因为不确定。volatile它有与锁雷同的作用:保障可见性和有序性,所不同的是,在原子性方面只保障写变量操作的原子性,单没有锁排他性。

2.Volatile关键字的作用
1.保障可见性
2.不保障原子性
3.禁止指令重排

咱们先解释第一点,保障可见性,可见性咱们要从JMM(JAVA内存模型)开始讲起

2.1 JMM(JAVA内存模型)的了解

JMM并不实在存在,它形容的是一组标准

JVM工作线程工作的时候,是有本人的工作内存的,工作内存是每个线程的公有区域,而java所有变量都保留在主存,主存是共享区域。线程在运行的时候,会先把变量拷贝一份到本人的工作内存,而后对变量进行赋值操作,操作实现后再将变量写会主存。并不能间接操作主内存变量,各个线程的工作内存中存储着主存的变量正本拷贝,因而不同的线程无法访问对方的工作主存,线程的通信必须通过内存来实现,这就是JMM。

2.2 Volatile的保障可见性
晓得了JMM线程模型之后,咱们就能够晓得,主内存的变量,都是要被拷贝到工作线程的工作空间后,再进行操作。如果此时主内存理得变量发生变化,工作线程是感知不到的,咱们能够用代码来进行示范:

public class MyData {
    int  number = 0;


    public void addTo60(){
        this.number = 60;
    }

    public void add(){
        this.number++;
    }

咱们先定义一个类,MyData,只有一个一般int变量。

再定义一个测试类:

public class VisibleTest {

    public static void main(String[] args) {
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "comm in");
            myData.number = myData.number ++;
            System.out.println("myData"+myData.number);
        }).start();


        while(myData.number==0){
            System.out.println(myData.number);
        }
        System.out.println(Thread.currentThread().getName()+"over");

    }
}

此时咱们通常会认为,number随着变量值的批改,会进行while循环的操作,然而后果并不会。

这是因为变量在拷贝回线程,批改值后并没有返回主存,导致另外一个线程感知不到,当初咱们加上Volatile关键字:

volatile int  number = 0;


线程顺利完结了!

这是因为加上volatile后,每次线程都要去主存来读取值,每次写完值后都要放回主存!

咱们查看字节码文件:

其中 getfield 和putfield 都是从主存中获取和批改值。

然而volatile只保障读和写的和见性,并没有保障写的排他性,所以也就没有保障原子性。

2.2.1 内存屏障指令

volatile之所以能保障可见性和避免重排序,是因为他的底层是内存屏障指令:

内存屏障,是一个cpu指令,作用有两个:
一是保障特定操作的执行程序
二是保障某些变量的内存可见性

因为编译器和处理器都能执行指令重排优化,如果在指令间插入一条memory barrier则会通知编译器和cpu
不论什么指令都不能和这条指令重排序,也就是说,通过内存屏障指令禁止在内存屏障前后的指令执行从新排序优化
内存屏障指令另一个作用是强制刷出各种cpu的缓存数据,因而任何cpu上的线程都能读取到这些数据的最新版本

2.3 Volatile禁止指令重排序

对于指令重排,咱们先来举一个例子:

        int x = 11; //1
        int y = 15; //2
        x = x + 5;  //3
        y = x * x;  //4

这是四条简略的代码,咱们都晓得最初x = 16,y = 16 * 16
如果咱们不晓得指令重排序的话,可能只是简略地认为,语句的执行程序为1234而已。

其实,计算机在执行程序的时候,为了进步性能,编译器和处理器经常会对指令进行重排,个别有以下三种:

源代码-> 编译器优化的重排->指令并行的重排->内存零碎的重排->最终执行的指令

单线程环境里确保程序最终执行后果和代码执行的后果统一:也就是说,单线程环境无论如何重排指令,最终的后果都是统一的。

处理器在进行重排序时必须思考指令之间的数据依赖性:也就是说,上述命令,4肯定在3前面,1肯定在3后面,不然就无奈保障最终一致性了。

好的,单线程环境下没什么问题,接下来咱们看多线程环境下:

两个线程同时运行时吗,就会呈现这种后果,也可能会呈现上面这种后果:

这样最终的后果就会不确定了,而volatile关键字就防止了指令重排序,依照编码的程序来进行编译执行!

3.用volitile改写单例模式
通常咱们认为的单例模式,在单线程下,都是这么写的

public class SingletomDeomo {

    private static volatile SingletomDeomo instance = null;

    private SingletomDeomo() {
        System.out.println(Thread.currentThread().getName() + "\t 构造方法singletom");
    }

       public static SingletomDeomo getInstance(){
           if(instance ==null){
               instance = new SingletomDeomo();
           }
           return instance;
       }
}

然而在多线程环境下,运行起来就会产生多个实例,那是因为因为上下文的切换,很多线程在判断完if为空后,工夫片被别的线程夺去,而后别的线程又new了instance,导致当初的线程夺回工夫片后,又会持续new一个对象。

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                SingletomDeomo.getInstance();
            }).start();
        }
    }

为了解决这个问题,咱们能够在这个办法上加上synchronize关键字,然而这样运行起来太重,咱们能够应用dcl(double check lock) 双重校验锁来解决。

 if (instance == null) {
            synchronized (SingletomDeomo.class) {
                if (instance == null) {
                    instance = new SingletomDeomo();
                }
            }
        }

此时,来源gao@daima#com搞(%代@#码网如果instance 不加volatile关键字,还是会有问题。

咱们要仔细分析一下instance = new SingletomDemo();里的步骤:

  • 1.memory = allocate();
  • 2.instance(memory);
  • 3.instance = memory;

大略就是这么三步:
1.调配一块内存区域
2.将这块内存区域初始化
3.将这块内存区域调配给instance

留神:此时,这三步也是能够被重排序的!

所以,在instance上加上volatile就没事了!

以上便是对volatile的学习笔记。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:JAVA并发编程Volatile关键字

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址