一文读懂ava中的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的关键字。

下面看下正确的用法:

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](https://github.com/ddean2009/learn-java-concurrency/tree/master/volatile)

总结

到此这篇关于一文读懂ava中的Volatile关键字使用的文章就介绍到这了,更多相关java volatile关键字内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java多线程编程之慎重使用volatile关键字

    volatile关键字相信了解Java多线程的读者都很清楚它的作用.volatile关键字用于声明简单类型变量,如int.float.boolean等数据类型.如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的.但这有一定的限制.例如,下面的例子中的n就不是原子级别的: 复制代码 代码如下: package mythread; public class JoinThread extends Thread{public static volatile int n = 0;p

  • 谈谈Java中Volatile关键字的理解

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机.volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情. 一.前言 JMM提供了volatile变量定义.final.synchronized块来保证可见性. 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值.volatile很容

  • Java关键字volatile和synchronized作用和区别

    volatile是变量修饰符,而synchronized则是作用于一段代码或方法:如下三句get代码: int i1; int geti1() {return i1;} volatile int i2; int geti2() {return i2;} int i3; synchronized int geti3() {return i3;} geti1() 得到存储在当前线程中i1的数值.多个线程有多个i1变量拷贝,而且这些i1之间可以相互不同.换句话说,另一个线程可能已经改变了它线程内的i1

  • 深入解析Java中volatile关键字的作用

    在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制. synchronized 同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized 修饰的方法 或者 代码块.

  • Java中volatile关键字的作用与用法详解

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile 关键字作用是,使系统中所有线程对该关键字修饰的变量共享可见,可以禁止线程的工作内存对volatile修饰的变量进行缓存. volatile 2个使用场景: 1.可见性:Java提供了volatile关键字来保证可见性. 当一个共享变量被volatile修饰时,它会保证修

  • 一文读懂ava中的Volatile关键字使用

    在本文中,我们会介绍java中的一个关键字volatile. volatile的中文意思是易挥发的,不稳定的.那么在java中使用是什么意思呢? 我们知道,在java中,每个线程都会有个自己的内存空间,我们称之为working memory.这个空间会缓存一些变量的信息,从而提升程序的性能.当执行完某个操作之后,thread会将更新后的变量更新到主缓存中,以供其他线程读写. 因为变量存在working memory和main memory两个地方,那么就有可能出现不一致的情况. 那么我们就可以使

  • 一文读懂JAVA中HttpURLConnection的用法

    针对JDK中的URLConnection连接Servlet的问题,网上有虽然有所涉及,但是只是说明了某一个或几个问题,是以FAQ的方式来解决的,而且比较零散,现在对这个类的使用就本人在项目中的使用经验做如下总结: 1:> URL请求的类别: 分为二类,GET与POST请求.二者的区别在于: a:) get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet, b:) post与get的不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内. 2:>

  • 一文读懂go中semaphore(信号量)源码

    运行时信号量机制 semaphore 前言 最近在看源码,发现好多地方用到了这个semaphore. 本文是在go version go1.13.15 darwin/amd64上进行的 作用是什么 下面是官方的描述 // Semaphore implementation exposed to Go. // Intended use is provide a sleep and wakeup // primitive that can be used in the contended case /

  • 一文读懂ES7中的javascript修饰器

    什么是修饰器 修饰器(Decorator)是ES7的一个提案,它的出现能解决两个问题: 不同类间共享方法 编译期对类和方法的行为进行改变 用法也很简单,就是在类或方法的上面加一个@符,在vue in typescript中经常用到 以上的两个用处可能不太明白,没关系,我们开始第一个例子 例子1:修饰类 @setProp class User {} function setProp(target) { target.age = 30 } console.log(User.age) 这个例子要表达的

  • 一文读懂C++中的继承之菱形继承(案例分析)

    前言 我们上一篇说了世间万物都有一个继承体制,或多或少子类继承了父类的某些特征,但大多都是单向继承,但是就有些特例他就是多继承,比如: 我们从图片中就可以看到,两栖动物它既继承了水生动物的一部分特性,也继承了陆地动物的一些特性,那么我们的代码,会不会也会有这种多继承现象呢,我们一起来看一下. 提示:以下是本篇文章正文内容,下面案例可供参考 一.什么是多继承? 1.单继承 我们来看一个图先了解一下单继承,再看有什莫区别 也就是说,一个子类只有一个直接父类时称这个继承关系为单继承 2.多继承 我们把

  • 一文读懂JavaScript 中的延迟加载属性模式

    传统上,开发人员在 JavaScript 类中为实例中可能需要的任何数据创建属性.对于在构造函数中随时可用的小块数据来说,这不是问题.但是,如果在实例中可用之前需要计算某些数据,您可能不想预先支付该费用.例如,考虑这个类: class MyClass { constructor() { this.data = someExpensiveComputation(); } } 在这里,data属性是作为执行一些昂贵计算的结果而创建的.如果您不确定是否会使用该属性,则预先执行该计算可能效率不高.幸运的

  • 一文读懂C++中指针和内存分配

    指针 指针是保存内存位置地址的变量.我们知道声明的所有变量在内存中都有一个特定的地址.声明一个指针变量来指向内存中的这些地址. 声明指针变量的一般语法是: int p, *ptr; //声明变量p和指针变量ptr p = 4; //赋值4给变量p ptr = &p; //将p的地址分配给指针变量ptr 在内存中,这些声明将表示如下: 这是指针在内存中的内部表示.当地址变量分配给指针变量时,它指向的变量如上图所示. 由于 ptr具有变量 p 的地址,*ptr 将给出变量 p 的值(指针变量 ptr

  • 一文读懂c++之static关键字

    一.静态变量 与C语言一样,可以使用static说明自动变量.根据定义的位置不同,分为静态全局变量和静态局部变量. 全局变量是指在所有花括号之外声明的变量,其作用域范围是全局可见的,即在整个项目文件内都有效.使用static修饰的全局变量是静态全局变量,其作用域有所限制,仅在定义该变量的源文件内有效,项目中的其他源文件中不能使用它. 块内定义的变量是局部变量,从定义之处开始到本块结束处为止是局部变量的作用域.使用static修饰的局部变量是静态局部变量,即定义在块中的静态变量.静态局部变量具有局

  • 一文读懂Java Iterator(迭代器)

    Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList和HashSet等集合. Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口. 迭代器 it 的两个基本操作是 next .hasNext 和 remove. 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态. 调用 it.hasNext() 用于检测集合中是否

  • 一文读懂vue动态属性数据绑定(v-bind指令)

    v-bind的基本用法 一.本节说明 前面的章节我们学习了如何向页面html标签进行插值操作,那么如果我们想动态改变html标签的属性,该怎么办呢? 这就是我们这节开始要讲的内容v-bind. 二. 怎么做 ":"为v-bind的简写形式,也可称为语法糖 三. 效果 四. 深入 在上图中将a标签的href属性值设置为toutiao,VUE实例将自动去data里面寻找toutiao属性进行值绑定. 不只是a标签,所有的html标签属性都可以通过v-bind进行值绑定,然后通过改变数据动态

随机推荐