热门标签 | HotTags
当前位置:  开发笔记 > 开发工具 > 正文

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

volatile关键字的作用保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。这篇文章主要介绍了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关键字内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 本文基于对相关论文和开源代码的研究,详细介绍了LOAM(激光雷达里程计与建图)的工作原理,并对其关键技术进行了分析。 ... [详细]
  • 本文详细记录了在基于Debian的Deepin 20操作系统上安装MySQL 5.7的具体步骤,包括软件包的选择、依赖项的处理及远程访问权限的配置。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 深入理解Java中的volatile、内存屏障与CPU指令
    本文详细探讨了Java中volatile关键字的作用机制,以及其与内存屏障和CPU指令之间的关系。通过具体示例和专业解析,帮助读者更好地理解多线程编程中的同步问题。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文介绍如何使用 Sortable.js 库实现元素的拖拽和位置交换功能。Sortable.js 是一个轻量级、无依赖的 JavaScript 库,支持拖拽排序、动画效果和多种插件扩展。通过简单的配置和事件处理,可以轻松实现复杂的功能。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了如何在 Spring Boot 应用中通过 @PropertySource 注解读取非默认配置文件,包括配置文件的创建、映射类的设计以及确保 Spring 容器能够正确加载这些配置的方法。 ... [详细]
  • This document outlines the recommended naming conventions for HTML attributes in Fast Components, focusing on readability and consistency with existing standards. ... [详细]
  • 在现代网络环境中,两台计算机之间的文件传输需求日益增长。传统的FTP和SSH方式虽然有效,但其配置复杂、步骤繁琐,难以满足快速且安全的传输需求。本文将介绍一种基于Go语言开发的新一代文件传输工具——Croc,它不仅简化了操作流程,还提供了强大的加密和跨平台支持。 ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 从零开始构建完整手机站:Vue CLI 3 实战指南(第一部分)
    本系列教程将引导您使用 Vue CLI 3 构建一个功能齐全的移动应用。我们将深入探讨项目中涉及的每一个知识点,并确保这些内容与实际工作中的需求紧密结合。 ... [详细]
author-avatar
学生联盟v
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有