博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java面试-谈谈你对volatile的理解
阅读量:6403 次
发布时间:2019-06-23

本文共 3705 字,大约阅读时间需要 12 分钟。

一、volatile特性:

volatile是Java虚拟机提供的轻量级的同步机制。主要有三大特性:

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排序

1、保证可见性

1)代码演示

AAA线程修改变量number的值为60,main线程获取到的number值是0,就一直循环等待。

原因:int number = 0;number变量之前没有添加volatile关键字,没有可见性。添加volatile关键字,可以解决可见性问题。

public class VolatileDemo {    int number = 0;    public void addTo60() {        this.number = 60;    }    //volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改    @Test    public void testVolatile() {        new Thread(() -> {            System.out.println(Thread.currentThread().getName() + " come in");            try {                TimeUnit.SECONDS.sleep(3);            } catch (InterruptedException e) {                e.printStackTrace();            }            addTo60();            System.out.println(Thread.currentThread().getName() + " update number value:" + number);        }, "AAA").start();        //第2个线程是main线程        while (number == 0) {            //main线程就一直等待循环,直到number的值不等于0        }        System.out.println(Thread.currentThread().getName() + " mission is over, main thread number value:" + number);    }}

2)volatile是如何来保证可见性的呢?

如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令。

  • 将这个变量所在缓存行的数据写回到系统内存。
  • 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

2、不保证原子性

1)代码演示

 volatile修饰number,进行number++操作,每次执行number的返回结果都不一样

public class VolatileDemo {    volatile int number = 0;    public void increase() {        number++;    }    public static void main(String[] args) {        VolatileDemo volatileDemo = new VolatileDemo();        for (int i = 0; i < 20; i++) {            new Thread(() -> {                for (int j = 0; j < 1000; j++) {                    volatileDemo.increase();                }            }).start();        }        //等待20个线程处理完,再用main线程取得最终返回结果        while (Thread.activeCount() > 2) {            Thread.yield();        }        System.out.println(Thread.currentThread().getName() + " finally number value:" + volatileDemo.number);    }  

2)volatile为什么不保证原子性?

 n++被拆分成3个指令:

getfield  从主内存中拿到原始值 iadd      在线程工作内存中进行加1操作 putfield  把累加后的值写回主内存 如果第二个线程在第一个线程读取旧值和写回新值期间读取n的值, 那么第二个线程就会与第一个线程看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败。

3)如何解决原子性问题

  • CAS机制:AtomicInteger number = AtomicInteger(0)
  • 锁机制:synchronized、Lock

3、禁止指令重排序

volatile的写-读与锁的释放-获取有相同的内存效果。

 

volatile写-读的内存语义:

当写一个volatile变量时,JMM会把线程A对应的本地内存中的共享变量值刷新到主内存。 当读一个volatile变量时,JMM会把线程B对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。 线程A写一个volatile变量,随后线程B读这个volatile变量,实质上是线程A通过主内存向线程B发送消息。
public class VolatileExample {    int a = 0;    volatile boolean flag = false;    public void writer() {        a = 1;        flag = true;    }    public void reader() {        if (flag) {            System.out.println("resultValue:" + a);        }    }}

 

为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序

volatile写插入内存屏障:

volatile读插入内存屏障:

 二、你在哪些地方用到过volatile

1、单例模式(双重检查锁DCL)

以下代码不一定线程安全,原因是有指令重排序的存在,某个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化 因为instance = new SingletonDemo();可以分为以下3步完成(伪代码)       memory = allocate(); //1.分配对象内存空间       instance(memory);    //2.初始化对象       instance = memory;   //3.设置instance指向刚分配的内存地址,此时instance!=null       步骤2和步骤3间可能会重排序 使用volatile禁止指令重排序,对volatile变量的写操作都先行发生于后面对这个变量的读操作
public class SignletonDemo {    private static SignletonDemo instance;    private SignletonDemo() {        System.out.println(Thread.currentThread().getName() + " 构造方法SingletonDemo");    }    public static SignletonDemo getInstance() {        //第一次检测        if (instance == null) {            //同步            synchronized (SignletonDemo.class) {                if (instance == null) {                    //多线程环境下可能会出现问题的地方                    instance = new SignletonDemo();                }            }        }        return instance;    }}

2、读写锁手写缓存

3、CAS JUC包中大量使用volatile

转载于:https://www.cnblogs.com/wjh123/p/11094691.html

你可能感兴趣的文章
标准的组件结构
查看>>
vue——一个页面实现音乐播放器
查看>>
SVG 扬帆起航
查看>>
NET Core-学习笔记(二)
查看>>
职业生涯上的点点滴滴
查看>>
Linux下添加新硬盘,分区及挂载
查看>>
一起来将vscode变成私人定制笔记本
查看>>
Flutter 云音乐
查看>>
RecyclerView实现多type页面
查看>>
个人的web商城网站
查看>>
debian fcitx
查看>>
排中律与实无穷问题的性质分析
查看>>
08/23 学习总结
查看>>
关于Ubuntu下安装phpmyadmin后mysqli丢失的解决
查看>>
物理层
查看>>
linux多网卡路由设置
查看>>
win7环境下的栈溢出与实战
查看>>
查看ios字体库方法
查看>>
八大监听器
查看>>
self.navigationController退出到指定页面,或者一次性pop出n个页面
查看>>