【JUC系列第一篇】-Volatile关键字及内存可见性
作者:毕来生
微信:878799579
- 什么是JUC?
JUC全称 java.util.concurrent 是在并发编程中很常用的实用工具类
2.Volatile关键字
1、如果一个变量被volatile关键字修饰,那么这个变量对所有线程都是可见的。
2、如果某条线程修改了被Volatile修饰的这个变量值,修改后的值对于其他线程来时是立即可见的。
3、并不是经过Volatile修饰过的变量在多线程下就是安全的
4、多线程间可以使用SynchronousQueue或者Exchanger进行数据之间传递
3.内存可见性
内存可见性(Memory Visibility)是指当某个线程正在使用对象状态 而另一个线程在同时修改该状态,需要确保当一个线程修改了对象 状态后,其他线程能够看到发生的状态变化。
可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。
原理同CAS原理相同,不懂的同学可以自行百度,附上一张CAS演示图供大家参考
4.实战举例
通过线程来修改变量count的值,使用Volatile关键字修饰和不使用Volatile修饰count变量结果对比。
首先我们来看一下通过内部类实现Runnable,变量使用Volatile关键字修饰演示以及结果
package org.bilaisheng.juc;
/**
* @Author: bilaisheng
* @Wechat: 878799579
* @Date: 2019/1/1 16:29
* @Todo: 通过内部类实现Runnable,变量使用Volatile关键字修饰演示
* @Version : JDK11 , IDEA2018
*/
public class NoVolatileTest{
public static void main(String[] args) {
NoVolatileThread noVolatileThread = new NoVolatileThread();
new Thread(noVolatileThread).start();
while (true){
if(noVolatileThread.isFlag()){
System.out.println("flag 此时为true !");
break;
}
}
}
}
class NoVolatileThread implements Runnable{
private boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println(Thread.currentThread().getName() + " flag = " + flag);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
运行结果如下图所示:
接下来我们来看一下通过内部类实现Runnable,变量不使用Volatile关键字修饰演示以及结果
package org.bilaisheng.juc;
/**
* @Author: bilaisheng
* @Wechat: 878799579
* @Date: 2019/1/1 16:53
* @Todo: 通过内部类实现Runnable,变量使用Volatile关键字修饰演示
* @Version : JDK11 , IDEA2018
*/
public class VolatileTest{
public static void main(String[] args) {
VolatileThread volatileThread = new VolatileThread();
new Thread(volatileThread).start();
while (true){
// if的判断volatile保证当时确实正确,然后线程a可能处于休眠状态,
// 线程b也判断不存在,b线程就new了一个。
// 然后a线程wake up,据需执行new volatile获取最新值。
if(volatileThread.isFlag()){
System.out.println("flag 此时为true !");
break;
}
}
}
}
class VolatileThread implements Runnable{
private volatile boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println(Thread.currentThread().getName() + " flag = " + flag);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
运行结果如下图所示:
通过对比我们发现在通过Volatile修饰和不通过Volatile修饰的变量,输出结果竟然会有些偏差。到底是为什么呢?
我们逐步拆解上面代码执行步骤:
1、针对于不使用Volatile关键字修饰变量:
- 步骤一:默认flag = false;
- 步骤二main线程的缓存区域没有刷新 flag的值。所以flag 还是false。故没有输出<flag 此时为true !>
- 步骤三:子线程输出 Thread-0 flag = true
2、针对于使用Volatile关键字修饰变量:
- 步骤一:默认flag = false;
- 步骤二:主线程看到flag是被Volatile关键字修饰的变量。则获取最新的flag变量值,此时flag = true。故输出<flag 此时为true !>
- 步骤三:子线程输出 Thread-0 flag = true
- Volatile的优点
可见性:被Volatile修饰的变量可以马上刷新主内存中的值,保证其他线程在获取时可以获取最新值,所有线程看到该变量的值均相同。
轻量级的synchronized,高并发下保证变量的可见性。
6.Volatile的缺点
1、频繁刷新主内存中变量,可能会造成性能瓶颈
2、不具备操作的原子性,不适合在对该变量的写操作依赖于变量本身自己。例如i++,并不能通过volatile来保证原子性
【JUC系列第一篇】-Volatile关键字及内存可见性的更多相关文章
- volatile关键字与内存可见性
前言 首先,我们使用多线程的目的在于提高程序的效率,但是如果使用不当,不仅不能提高效率,反而会使程序的性能更低,因为多线程涉及到线程之间的调度.CPU上下文的切换以及包括线程的创建.销毁和同步等等,开 ...
- volatile关键字与内存可见性&原子变量与CAS算法
1 .volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 2 .原子变量:jdk1.5后java.util.concurrent.atomic 包下提供常用的原子变量 ...
- volatile关键字及内存可见性
先看一段代码: package com.java.juc; public class TestVolatile { public static void main(String[] args) { T ...
- 详解volatile 关键字与内存可见性
先来看一个例子: public class VolatileTest { public static void main(String[] args) { T ...
- 深入理解javascript函数系列第一篇——函数概述
× 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...
- 【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念
volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...
- 深入理解javascript函数系列第一篇
前面的话 函数对任何一门语言来说都是核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即对象,程序可以随意操控它们.函数可以嵌套在其他函数中 ...
- volatile关键字解析&内存模型&并发编程中三概念
原文链接: http://www.cnblogs.com/dolphin0520/p/3920373.html volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java5之前,它是一个 ...
- 深入学习jQuery选择器系列第一篇——基础选择器和层级选择器
× 目录 [1]id选择器 [2]元素选择器 [3]类选择器[4]通配选择器[5]群组选择器[6]后代选择器[7]兄弟选择器 前面的话 选择器是jQuery的根基,在jQuery中,对事件处理.遍历D ...
随机推荐
- python计算平面的法向-利用协方差矩阵求解特征值和特征向量
Obvious,最小特征值对应的特征向量为平面的法向 这个问题还有个关键是通过python求协方差矩阵的特征值和特征向量,np.linalg.eig()方法直接返回了特征值的向量和特征向量的矩阵 sc ...
- 【Linux内核】编译与配置内核(x86)
[Linux内核]编译与配置内核(x86) https://www.cnblogs.com/jamesharden/p/6414736.html
- centos7.6 yum安装mysql5.7版本
由于mysql5.5及之前的版本一些项目上线报错 卸载: 首先删除centos上原来的mysql老版本,注意备份,清理干净. 之前怎么安装的清理,防止卸载不干净会有冲突. 我之前yum安装mysql5 ...
- 『Python基础』第4节:基础数据类型初识
本节只是对基础数据类型做个简单介绍, 详情会在之后慢慢介绍 什么是数据类型? 我们人类可以分清数字与字符串的区别, 可是计算机不能. 虽然计算机很强大, 但在某种程度上又很傻, 除非你明确告诉它数字与 ...
- SAS学习笔记7 合并语句(set、merge函数)
set函数:纵向合并数据集 set语句进行纵向合并.set语句的作用是将若干个数据集依次纵向连接,并存放到data语句建立的数据集中.若set后面只有一个数据集,此时相当于复制的作用 注:data语句 ...
- k8s-gitlab搭建
Gitlab官方提供了 Helm 的方式在 Kubernetes 集群中来快速安装,但是在使用的过程中发现 Helm 提供的 Chart 包中有很多其他额外的配置,所以我们这里使用自定义的方式来安装, ...
- IDEA忽略不必要提交的文件
1.在idea中安装插件用来生成和管理 .gitignore 文件,安装成功后重启idea 2.新建.gitignore 文件 3.将不需要提交的文件添加到.gitignore 4.删除缓冲文件 . ...
- qt翻译和国际化的探讨。
这段时间一直都在怼qt的国际化,以前也接触过国际化,但是感觉不是那么的深刻,这次是因为一个项目要做一个国际化的版本,代码里面是不能出现中文的,所以就翻译了一下.qt用的是4.8.6 64位的,IDE( ...
- C# xml序列化 datatime字段
[XmlIgnore] public DateTime ApplicationDatetime { get; set; } [XmlElement("ApplicationDatetime& ...
- (九)springmvc之json的数据请求(客户端发送json数据到服务端)
index.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pag ...