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关键字使用的更多相关文章

  1. java中的volatile关键字

    java中的volatile关键字 一个变量被声明为volatile类型,表示这个变量可能随时被其他线程改变,所以不能把它cache到线程内存(如寄存器)中. 一般情况下volatile不能代替syn ...

  2. Java中的volatile关键字的功能

    Java中的volatile关键字的功能 volatile是java中的一个类型修饰符.它是被设计用来修饰被不同线程访问和修改的变量.如果不加入volatile,基本上会导致这样的结果:要么无法编写多 ...

  3. 深入理解Java中的volatile关键字

    在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...

  4. Java中的volatile关键字为什么不是不具有原子性

    Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值是原子操作,为什么?为什么volatile能替代简单的锁,却不能保证原子性?这里面涉及volat ...

  5. 精通Java中的volatile关键字

    在一些开源的框架的源码当中时不时都可以看到volatile这个关键字,最近特意学习一下volatile关键字的使用方法. 很多资料中是这样介绍volatile关键字的: volatile是轻量级的sy ...

  6. CPU缓存一致性协议与java中的volatile关键字

    有关缓存一致性协议MESI自行百度. 提出问题:volatile在缓存一致性协议上又做了哪些事情?为啥它不保证原子性? 在缓存一致性协议下,CPU为了执行效率使用了写(存储)缓存和失效队列从而导致对用 ...

  7. zz剖析为什么在多核多线程程序中要慎用volatile关键字?

    [摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...

  8. java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)

    概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...

  9. 再议Java中的static关键字

    再议Java中的static关键字 java中的static关键字在很久之前的一篇博文中已经讲到过了,感兴趣的朋友可以参考:<Java中的static关键字解析>. 今天我们再来谈一谈st ...

随机推荐

  1. RabbitMQ的高可用集群部署

    RabbitMQ的高可用集群部署 标签(空格分隔): 消息队列 部署 1. RabbitMQ部署的三种模式 1.1 单一模式 单机情况下不做集群, 仅仅运行一个RabbitMQ. # docker-c ...

  2. Unity 游戏框架搭建 2019 (二十七) 弃用的代码警告解决

    在前两篇,我们把所有的示例重头到尾整理了一遍. 当前的状态如下: 要做的事情: (完成) 备份:导出文件,并取一个合理的名字. 遗留问题: (完成) 第八个示例与之前的示例代码重复,功能重复. (完成 ...

  3. shell查询目标jvm的perm占比

    #查询指定进程号下面的方法区使用率,jdk1.7是perm,jdk1.8是metaspace function get_perm_use_percent() { pid="$1" ...

  4. Mysql数据库错误代码大全

                                                    Mysql数据库错误代码大全 出现较多的一些网页代码提示的意思: 1016错误:文件无法打开,使用后台修 ...

  5. Spring Framework之事务管理

    目录 问题 数据库事务 事务的定义 事务的目的 事务的特性 事务隔离级别 数据并发问题 事务隔离级别对数据并发问题的作用 快照读 Spring事务管理 事务管理接口 TransactionDefini ...

  6. django中设置定时任务

    django中设置定时任务 在django中设置定时任务我们可以借用django-crontab这个第三包来实现 django-crontab只能在linux系统下使用 安装: pip install ...

  7. flask-redirect

    flask-redirect from flask import Flask, url_for, request, redirect app = Flask(__name__) @app.route( ...

  8. Python zipfile模块学习

    转载自https://www.j4ml.com/t/15270 import zipfile import os from zipfile import ZipFile class ZipManage ...

  9. MODIS系列之NDVI(MOD13Q1)四:MRT单次及批次处理数据

    前言: 本篇文章的出发点是因为之前接触过相关研究,困囧于该系列资料匮乏,想做一个系列.个人道行太浅,不足之处还请见谅.愿与诸君共勉. 数据准备: MODIS数据产品MOD13Q1—以2010年河南省3 ...

  10. 百度AI开发平台简介

    AIstudio https://aistudio.baidu.com/aistudio/index 关于AI Studio AI Studio是基于百度深度学习平台飞桨的一站式AI开发平台,提供在线 ...