一、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可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
public static void main(String[] args) {
VolatileDemo volatileDemo = new VolatileDemo();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " come in");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatileDemo.addTo60();
System.out.println(Thread.currentThread().getName() + " update number value:" + volatileDemo.number); }, "AAA").start(); //第2个线程是main线程
while (volatileDemo.number == 0) {
//main线程就一直等待循环,直到number的值不等于0 }
System.out.println(Thread.currentThread().getName() + " mission is over, main thread number value:" + volatileDemo.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();
} // 默认有 main 线程和 gc 线程
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " finally number value:" + volatileDemo.number);
}
}

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

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

getfield  从主内存中拿到原始值
iadd 在线程工作内存中进行加1操作
putfield 把累加后的值写回主内存
如果第二个线程在第一个线程读取旧值和写回新值期间读取number的值,
那么第二个线程就会与第一个线程看到同一个值,并执行相同值的加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

p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
span.s1 { font: 11.5px Helvetica; color: rgba(255, 102, 0, 1) }

java面试-谈谈你对volatile的理解的更多相关文章

  1. java面试-谈谈你对OOM的理解

    一.OOM(OutOfMemoryError): 对象无法释放或无法被垃圾回收,造成内存浪费,导致程序运行速度减慢,甚至系统崩溃等严重后果,就是内存泄漏.多个内存泄漏造成可使用内存变少,会导致内存溢出 ...

  2. java基础-谈谈你对面向对象的理解

    一 前言 本篇文章的核心知识如下,主要是帮助大家更好的理解面向对象编程: 二面向对象VS面向过程 2.1 面向过程编程 面向过程编程(Process Oriented Programming )其意指 ...

  3. 谈谈你对volatile的理解

    1.volatile是Java虚拟机提供的轻量级的同步机制 1.1保证可见性1.2不保证原子性1.3禁止指令重排 JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象 ...

  4. 面试:谈谈你对jQuery的理解

    jQuery是一个轻量级的javascript框架,极大的简化了js的编程. 1.首先jQuery提供了强大的元素选择器.用于获取html页面中封装了html元素的jQuery对象.像常见的选择器有: ...

  5. 对volatile的理解--从JMM以及单例模式剖析

    请谈谈你对volatile的理解 1.volitale是Java虚拟机提供的一种轻量级的同步机制 三大特性1.1保证可见性 1.2不保证原子性 1.3禁止指令重排 首先保证可见性 1.1 可见性 概念 ...

  6. 【Java面试】面试遇到宽泛的问题,这么回答就稳了,谈谈你对Redis的理解

    "谈谈你对Redis的理解"! 面试的时候遇到这类比较宽泛的问题,是不是很抓狂? 是不是不知道从何开始说起? 没关系,今天我用3分钟教你怎么回答. 大家好,我是Mic,一个工作了1 ...

  7. Java面试官最常问的volatile关键字

    在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯 ...

  8. java面试总躲不过的并发(二):volatile原理 + happens-before原则

    一.happens-before原则 同一个线程中的,前面的操作 happens-before 后续的操作.(即单线程内按代码顺序执行.但是,在不影响在单线程环境执行结果的前提下,编译器和处理器可以进 ...

  9. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

随机推荐

  1. Dart Web

    Dart Web Dart for Web https://dart.dev/platforms dart2js xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允 ...

  2. Android混合Flutter

    官方文档 实验性:将Flutter添加到Android 测试仓库 取决于模块的源代码 方法测试成功

  3. 「NGK每日快讯」11.25日NGK公链第23期官方快讯!

  4. 从几个问题开始理解CFS调度器

    本文转载自从几个问题开始理解CFS调度器 导语 CFS(完全公平调度器)是Linux内核2.6.23版本开始采用的进程调度器,它的基本原理是这样的:设定一个调度周期(sched_latency_ns) ...

  5. 二分图最小点覆盖构造方案+König定理证明

    前言 博主很笨 ,如有纰漏,欢迎在评论区指出讨论. 二分图的最大匹配使用 \(Dinic\) 算法进行实现,时间复杂度为 \(O(n\sqrt{e})\),其中, \(n\)为二分图中左部点的数量, ...

  6. 从跳频技术聊CDMA/WIFI之母海蒂·拉玛传奇的一生

    导语:本篇的内容都是 文末的参考文章摘要而来的,本人根据自己的癖好,以及对 海蒂·拉玛 人生的感慨整理成本文. "WiFi"之母的海蒂·拉玛在中国的知名度,比起克劳德·香农应该也不 ...

  7. CSS的定位布局(position)

    定位 static(默认值) 没有开启定位 relative 相对定位的性质 包含块(containing block)概念 没有开启定位时包含块就是当前元素最近的祖先块元素 开启绝对定位后的元素包含 ...

  8. 后端程序员之路 32、Index搜索引擎实现分析1-类的设计

    # 1.forward_index 正排索引(正向索引)- filter_t- filter_judge # 2.inverted_index 倒排索引(反向索引)- inverted_pre_sco ...

  9. go map嵌套 map的value可以是任意类型

    在日常编程中,除了使用内置的数据类型,还会使用一些复杂的自定义数据类型,比如map K为string,V为数组.先了解一下go对map的基本设定: map的key可以是任意内置的数据类型(如int), ...

  10. web服务器-并发服务器2

    阅读目录 1.Web静态服务器-5-非堵塞模式 2.Web静态服务器-6-epoll 3.Web静态服务器-7-gevent版 4.知识扩展-C10K问题 一.Web静态服务器-5-非堵塞模式 单进程 ...