首页 > 其他 > 详细

并发编程基础之volatile

时间:2020-04-27 22:49:20      阅读:59      评论:0      收藏:0      [点我收藏+]

volatile

1、volatile

保证多线程数据的可见性

2、机器硬件CPU与JMM

(1) CPU 缓存模型

技术分享图片

技术分享图片

程序的局部执行原理

(2) CPU缓存一致性问题

每个CPU修改内存数据的步骤

  • 1、从内存中把数据读到cache中,并放到寄存器中进行操作
  • 2、在cache中更新数据
  • 3、把更新的结果刷新到内存中

一个CPU读数据,进行操作,另一个CPU写数据,就会产生数据不一致问题。

解决方案

  • 1、总线加锁

地址总线 数据总线 控制总线,对总线进行加锁时,其他CPU无法进行使用,会造成效率低。

  • 2、MESI缓存一致性协议

a、读操作:不做任何操作,把cache中的数据读到寄存器

b、写操作:发出信号,通知其他CPU,将该变量的CacheLine置为无效状态,其他CPU要访问这个变量的时候,只能从内存中获取。

Cache line 是cpu的一种实现机制,CPU的cache中会增加很多的Cache line。

(3)、java内存模型

技术分享图片

  • 主内存中的数据所有线程都可以进行访问(共享数据)
  • 每个线程都有自己的工作空间(本地内存)(私有数据)
  • 工作空间数据:局部变量、内存的副本
  • 线程不能直接修改内存中的数据,只能读到工作空间来修改,修改完成后刷新到内存

3、Volatile关键子的语义分析

volatile作用

让其他线程能够马上感知到某一线程对某个变量的修改

当一个线程对volatile修饰的变量进行修改时,会通过JMM控制向其他线程发出信号,映射到硬件中就是使Cache line置为无效

保证可见性

对共享变量的修改,其他线程马上能感知到,不能保证原子性。

多个原子性操作合在一起,就不存在原子性

public class Increa {

    static volatile int i = 0;

    public static void incre() {
        i++;
    }

    public static void main(String[] args) {
        for (int j = 0; j < 10; j++) {
            new Thread(() -> {
                for (int k = 0; k < 5; k++) {
                    incre();
                }
            }).start();
        }
        System.out.println(i);
    }
}
//这里输出不是50,证明volatile不能保证原子性,i++操作为非原子操作

保证有序性

禁止重排序

重排序(编译阶段,指令优化阶段)

输入程序的代码顺序并不是实际执行的顺序

++重排序后对单线程没有影响,对多线程有影响++。

对于volatile修饰的变量:

  • volatile之前的代码不能调整到它的后面;
  • volatile之后的代码不能调整到它的前面(as-if-seria);
  • volatile修饰的代码位置不变化;

volatile的原理和实现机制

使用hsdis汇编工具 反编译

java->class->JVM->ASM文件(汇编文件)

volatile int a=0;
代码映射到汇编语言
Lock:a

技术分享图片

总结:java中volatile具有的功能:

  • 1.可见性。
  • 2.防止JIT或者java解释器对指令重排序。
  • 3.内存屏障的功能,java解释器遇到volatile变量,会在volatile变量赋值之后,加一个lock addl $0x0,(%rsp)具有内存屏障功能的指令,防止内存重排序

4、volatile的使用场景

状态标志(开关模式)

public class ShutDowsnDemmo extends Thread{
    private volatile boolean started=false;

    @Override
    public void run() {
        while(started){
            dowork();
        }
    }
    public void shutdown(){
        started=false;
    }
}

双重检查锁定(double-checked-locking)

public class Singleton {
    private volatile static Singleton instance;
    public static Singleton getInstance(){
        if(instance==null){
            synchronized (Singleton.class){
                instance=new Singleton();
            }
        }
        return instance;
    }
}

需要利用顺序性

5、volatile与synchronized的区别

(1)使用上的区别

Volatile只能修饰变量,synchronized只能修饰方法和语句块

(2)对原子性的保证

synchronized可以保证原子性,Volatile不能保证原子性

(3)对可见性的保证

都可以保证可见性,但实现原理不同
Volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM

(4)对有序性的保证

Volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行

(5)其他

synchronized引起阻塞
Volatile不会引起阻塞

并发编程基础之volatile

原文:https://www.cnblogs.com/wilsonsui/p/12790677.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!