一、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. css 设置多行文本的行间距

    css 设置多行文本的行间距 block element span .ticket-card-info{ line-height:16px; display: inline-block; } .tic ...

  2. VSCode & useful Extensions

    VSCode & useful Extensions Code Spell Checker bug user dictionary https://www.cnblogs.com/xgqfrm ...

  3. Error Code: 1452 Cannot add or update a child row: a foreign key constraint fails

    错误: Error Code: 1452 Cannot add or update a child row: a foreign key constraint fails 错误产生情景:我向一张带外键 ...

  4. 详解Go语言调度循环源码实现

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/448 本文使用的go的源码15.7 概述 提到"调度&q ...

  5. 【HTB靶场系列】靶机Carrier的渗透测试

    出品|MS08067实验室(www.ms08067.com) 本文作者:大方子(Ms08067实验室核心成员) Hack The Box是一个CTF挑战靶机平台,在线渗透测试平台.它能帮助你提升渗透测 ...

  6. TERSUS无代码开发(笔记09)-简单实例前端样式设计

    前端常用样式设计 =========================================================================================== ...

  7. Tango with django 1.9 中文——3.Django基础

    让我们开始运用Django.本章主要是给你一个关于创建新项目和新应用过程的概览.在本章的末尾,你将建立起一个简单的由Django驱动的网站. 3.1 配置测试 让我们测试以下你的Python和Djan ...

  8. Spring中的依赖查找和依赖注入

    作者:Grey 原文地址: 语雀 博客园 依赖查找 Spring IoC 依赖查找分为以下几种方式 根据 Bean 名称查找 实时查找 延迟查找 根据 Bean 类型查找 单个 Bean 对象 集合 ...

  9. vue 递归调用组件出错

    报错信息: Avoid mutating an injected value directly since the changes will be overwritten whenever the p ...

  10. 干货!!!测试如何确定是前端bug还是后端bug

    目前的项目大多数都是前后端分离的,当我们发现bug后不知道指派给哪位开发,指派错了不仅影响解决bug 的效率,还容易被开发怼.最主要的是人家会认为你不专业,不专,不专呀.废话少说,上干货(踩过的坑)! ...