java中的Volatile关键字使用
java中的Volatile关键字使用
在本文中,我们会介绍java中的一个关键字volatile。 volatile的中文意思是易挥发的,不稳定的。那么在java中使用是什么意思呢?
我们知道,在java中,每个线程都会有个自己的内存空间,我们称之为working memory。这个空间会缓存一些变量的信息,从而提升程序的性能。当执行完某个操作之后,thread会将更新后的变量更新到主缓存中,以供其他线程读写。
因为变量存在working memory和main memory两个地方,那么就有可能出现不一致的情况。 那么我们就可以使用Volatile关键字来强制将变量直接写到main memory,从而保证了不同线程读写到的是同一个变量。
什么时候使用volatile
那么我们什么时候使用volatile呢?当一个线程需要立刻读取到另外一个线程修改的变量值的时候,我们就可以使用volatile。我们来举个例子:
public class VolatileWithoutUsage {
private int count = 0;
public void incrementCount() {
count++;
}
public int getCount() {
return count;
}
}
这个类定义了一个incrementCount()方法,会去更新count值,我们接下来在多线程环境中去测试这个方法:
@Test
public void testWithoutVolatile() throws InterruptedException {
ExecutorService service= Executors.newFixedThreadPool(3);
VolatileWithoutUsage volatileWithoutUsage=new VolatileWithoutUsage();
IntStream.range(0,1000).forEach(count ->service.submit(volatileWithoutUsage::incrementCount) );
service.shutdown();
service.awaitTermination(1000, TimeUnit.MILLISECONDS);
assertEquals(1000,volatileWithoutUsage.getCount() );
}
运行一下,我们会发现结果是不等于1000的。
java.lang.AssertionError:
Expected :1000
Actual :999
这是因为多线程去更新同一个变量,我们在上篇文章也提到了,这种情况可以通过加Synchronized关键字来解决。
那么是不是我们加上Volatile关键字后就可以解决这个问题了呢?
public class VolatileFalseUsage {
private volatile int count = 0;
public void incrementCount() {
count++;
}
public int getCount() {
return count;
}
}
上面的类中,我们加上了关键字Volatile,我们再测试一下:
@Test
public void testWithVolatileFalseUsage() throws InterruptedException {
ExecutorService service= Executors.newFixedThreadPool(3);
VolatileFalseUsage volatileFalseUsage=new VolatileFalseUsage();
IntStream.range(0,1000).forEach(count ->service.submit(volatileFalseUsage::incrementCount) );
service.shutdown();
service.awaitTermination(5000, TimeUnit.MILLISECONDS);
assertEquals(1000,volatileFalseUsage.getCount() );
}
运行一下,我们会发现结果还是错误的:
java.lang.AssertionError:
Expected :1000
Actual :992
~~
为什么呢? 我们先来看下count++的操作,count++可以分解为三步操作,1. 读取count的值,2.给count加1, 3.将count写回内存。添加Volatile关键词只能够保证count的变化立马可见,而不能保证1,2,3这三个步骤的总体原子性。 要实现总体的原子性还是需要用到类似Synchronized的关键字。
下面看下正确的用法:
~~~java
public class VolatileTrueUsage {
private volatile int count = 0;
public void setCount(int number) {
count=number;
}
public int getCount() {
return count;
}
}
@Test
public void testWithVolatileTrueUsage() throws InterruptedException {
VolatileTrueUsage volatileTrueUsage=new VolatileTrueUsage();
Thread threadA = new Thread(()->volatileTrueUsage.setCount(10));
threadA.start();
Thread.sleep(100);
Thread reader = new Thread(() -> {
int valueReadByThread = volatileTrueUsage.getCount();
assertEquals(10, valueReadByThread);
});
reader.start();
}
Happens-Before
从java5之后,volatile提供了一个Happens-Before的功能。Happens-Before 是指当volatile进行写回主内存的操作时,会将之前的非volatile的操作一并写回主内存。
public class VolatileHappenBeforeUsage {
int a = 0;
volatile boolean flag = false;
public void writer() {
a = 1; // 1 线程A修改共享变量
flag = true; // 2 线程A写volatile变量
}
}
上面的例子中,a是一个非volatile变量,flag是一个volatile变量,但是由于happens-before的特性,a 将会表现的和volatile一样。
本文的例子可以参考:https://github.com/ddean2009/learn-java-concurrency/tree/master/volatile
更多教程请参考 flydean的博客
java中的Volatile关键字使用的更多相关文章
- java中的volatile关键字
java中的volatile关键字 一个变量被声明为volatile类型,表示这个变量可能随时被其他线程改变,所以不能把它cache到线程内存(如寄存器)中. 一般情况下volatile不能代替syn ...
- Java中的volatile关键字的功能
Java中的volatile关键字的功能 volatile是java中的一个类型修饰符.它是被设计用来修饰被不同线程访问和修改的变量.如果不加入volatile,基本上会导致这样的结果:要么无法编写多 ...
- 深入理解Java中的volatile关键字
在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...
- Java中的volatile关键字为什么不是不具有原子性
Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值是原子操作,为什么?为什么volatile能替代简单的锁,却不能保证原子性?这里面涉及volat ...
- 精通Java中的volatile关键字
在一些开源的框架的源码当中时不时都可以看到volatile这个关键字,最近特意学习一下volatile关键字的使用方法. 很多资料中是这样介绍volatile关键字的: volatile是轻量级的sy ...
- CPU缓存一致性协议与java中的volatile关键字
有关缓存一致性协议MESI自行百度. 提出问题:volatile在缓存一致性协议上又做了哪些事情?为啥它不保证原子性? 在缓存一致性协议下,CPU为了执行效率使用了写(存储)缓存和失效队列从而导致对用 ...
- zz剖析为什么在多核多线程程序中要慎用volatile关键字?
[摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...
- java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)
概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...
- 再议Java中的static关键字
再议Java中的static关键字 java中的static关键字在很久之前的一篇博文中已经讲到过了,感兴趣的朋友可以参考:<Java中的static关键字解析>. 今天我们再来谈一谈st ...
随机推荐
- CentOS下的Docker-Compose离线安装
公司服务器已经安装了 Docker 环境,但没有安装 Docker Compose,使用起来十分不便.由于服务无法连接外网,下面演示如何离线安装 Docker Compose. (1)首先访问 doc ...
- Java 添加、读取和删除 Excel 批注
批注是一种富文本注释,常用于为指定的Excel单元格添加提示或附加信息. Free Spire.XLS for Java 为开发人员免费提供了在Java应用程序中对Excel文件添加和操作批注的功能. ...
- MYSQL索引类型。MYSQLc储存引擎
MYSQL索引类型,MYSQLc储存引擎 MySQL索引创建与删除 MySQL存储引擎的 ...
- python基本知识点if、while、等等
给予Python的相关知识点: 主要是对于基本语句的相关使用if else while for等先关的应用,以及步骤如下: 尝试编程如下:还有就是对于 import math import rando ...
- 《闲扯Redis四》List数据类型底层编码转换
一.前言 Redis 提供了5种数据类型:String(字符串).Hash(哈希).List(列表).Set(集合).Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要. ...
- "按钮"组件:<h-button> —— 快应用组件库H-UI
 <import name="h-button" src="../Common/ui/h-ui/basic/c_button"></impo ...
- Linux学习6-安装Python3.6
前言 今天我们来学习一下如何在Linux环境下安装Python3.6吧! 一:下载Python3.6(我下载的是python3.6.8) 地址:https://www.python.org/downl ...
- BFC的理解与应用
什么是BFC(Block formatting contexts) BFC的通俗理解: 首先BFC是一个名词,是一个独立的布局环境,我们可以理解为一个箱子(实际上是看不见摸不着的),箱子里面物品的摆放 ...
- javascript的数据类型(基本和复杂)
一.基本数据类型 string number boolean 二.复杂数据类型 Array Date object RegExp Sting Number Boolean 核心:Object fun ...
- 传智博客2015年最新版iOS基础视频_最适合初学者入门
视频介绍: 本视频是iOS学院精心录制的免费精华版iOS语言基础视频,该视频特点在于最大程度保证了知识点的完整性,按知识点进行视频录制,每个视频控制在20分钟左右,不会使学生产生疲劳,授课讲究通俗易懂 ...