一文精通Java中的volatile关键字

 更新时间:2019年05月07日 09:39:14   作者:架构与我  
volatile是java中的关键词之一,这篇文章主要给大家介绍了关于Java中volatile关键字的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

在一些开源的框架的源码当中时不时都可以看到volatile这个关键字,最近特意学习一下volatile关键字的使用方法。

volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。 相较于 synchronized 是一种较为轻量级的同步策略。

缺点:

1. volatile 不具备“互斥性”

2. volatile 不能保证变量的“原子性”

很多资料中是这样介绍volatile关键字的:

volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。

文字不太好理解,通过例子来理解。

1、例子

首先看一个没有使用volatile关键字例子:

package com.swnote.java;

/**
 * volatile测试例子
 *
 * @author lzj
 * @date [2019-04-47]
 */
public class VolatileTest {

 private boolean flag;

 public static void main(String[] args) {
 VolatileTest test = new VolatileTest();
 test.test();
 }

 public void test() {
 new Thread(() -> {
  try {
  Thread.sleep(1000L);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  flag = true;
 }).start();

 new Thread(() -> {
  while (true) {
  if (flag) {
   System.out.println("thread flag = " + flag);
  }
  }
 }).start();
 }
}

该例子中定义了一个flag共享变量,test方法里面开启了两个线程,第一个线程在等待1秒中后修改共享变量flag的值为true,第二个线程通过循环判断flag的值,当flag的值为true时,输出内容。

此时有两种猜想:

  • 执行后,可以看到输出内容,即说明第二个线程能够感知到第一个线程对共享变量flag的修改
  • 执行后,没有任务内容,即说明第二个线程无法感知到第一个线程对共享变量flag的修改

然后执行结果为:

没有任务的输出内容,即证明了此时这样情况下第二个线程无法感知到第一个线程对共享变量flag的修改的

现在修改一下例子,即为flag变量加上volatile关键字,即:

private volatile boolean flag;

然后再运行,此时结果为:

此时就有内容输出了,说明加上volatile关键字后,第二个线程可以感知到第一个线程对共享变量flag的修改的,这就是上面概念中所说的volatile在多处理器开发中保证了共享变量的“可见性”。

2、原理

通过上面的例子证明了volatile在多处理器开发中保证了共享变量的“可见性”,那它是怎么实现的呢?

这时就得介绍一下Java的内存模型了,首先看如下示意图:

Java内存模型是如上面所示的:

共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在自己的本地内存中进行,而不能直接读写主内存中的变量。

根据此理解,上述例子的在没有加volatile时的情况是这样的:

第一个线程从主内存中获取共享变量flag的值,此时值为false,将该值放到自己的本地内存中,然后对变量进行修改,将值改为true,此时也只是将本地内存中flag的值改为了true,此时还没有将值同步到主内存中,然后第二线程也是将共享变量flag的值放到自己的本地内存中,而此时flag的值还是为false,所以就是一直没有内容输出了。

然而加上volatile关键字后,第一个线程对flag的修改会强制刷新到主内存中去,同时还会导致其他线程中的本地内存的值会无效,需要重新到主内存获取,这样就保证了第一个线程对flag修改后,第二线程能够感知到。

3、注意点

volatile是轻量级的synchronized,但是它是不能够代替synchronized的,因为volatile只能保证原子性操作的安全,对于复合操作,volatile是不能保证线程安全的。

例如:

package com.swnote.java;

/**
 * 复合操作例子
 *
 * @author lzj
 * @date [2019-04-27]
 */
public class StatisticTest {
 private volatile int num = 0;

 public static void main(String[] args) {
  StatisticTest test = new StatisticTest();
  test.statistic();
 }

 public void statistic() {
  for (int i = 0; i < 20; i++) {
   new Thread(() -> {
    num++;
   }).start();
  }

  System.out.println("num = " + num);
 }
}

期望的运行结果是20,可是几乎每次运行结果都是不一样的,例如有的结果为:

这是因为num++这个操作不是原子性的,所以即使使用了volatile关键字,也是不能保证安全的。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • RocketMq同组消费者如何自动设置InstanceName

    RocketMq同组消费者如何自动设置InstanceName

    这篇文章主要介绍了RocketMq同组消费者如何自动设置InstanceName问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • 详解MyBatisPlus如何实现分页和查询操作

    详解MyBatisPlus如何实现分页和查询操作

    这篇文章主要为大家详细介绍了MyBatisPlus是如何实现分页和查询操作的,文中的示例代码讲解详细,对我们学习有一定的帮助,需要的可以参考一下
    2022-05-05
  • SpringMVC入门实例

    SpringMVC入门实例

    这篇文章主要介绍了SpringMVC入门实例,在springmvc入门教程里算是比较不错的,结构也比较完整,需要的朋友可以参考。
    2017-11-11
  • Spring解决循环依赖问题及三级缓存的作用

    Spring解决循环依赖问题及三级缓存的作用

    这篇文章主要介绍了Spring解决循环依赖问题及三级缓存的作用,所谓的三级缓存只是三个可以当作是全局变量的Map,Spring的源码中大量使用了这种先将数据放入容器中等使用结束再销毁的代码风格
    2022-07-07
  • springboot如何自定义pom内子依赖版本

    springboot如何自定义pom内子依赖版本

    这篇文章主要介绍了springboot如何自定义pom内子依赖版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 关于Spring Boot动态权限变更问题的实现方案

    关于Spring Boot动态权限变更问题的实现方案

    这篇文章主要介绍了Spring Boot动态权限变更实现的整体方案使用session作为缓存,结合AOP技术进行token认证和权限控制,本文给大家介绍的非常详细,需要的朋友参考下吧
    2021-06-06
  • 基于Java汇总Spock框架Mock静态资源经验

    基于Java汇总Spock框架Mock静态资源经验

    这篇文章主要介绍了基于Java汇总Spock框架Mock静态资源经验,前面讲了 Spock框架Mock对象、方法经验总结,今天分享一下Spock框架中Mock静态资源的实践经验汇总。分成静态资源和混合场景,需要的朋友可以参考一下
    2022-02-02
  • idea中的jvm调优方式

    idea中的jvm调优方式

    这篇文章主要介绍了idea中的jvm调优方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java微信公众平台开发(6) 微信开发中的token获取

    Java微信公众平台开发(6) 微信开发中的token获取

    这篇文章主要为大家详细介绍了Java微信公众平台开发第六步,微信开发中的token获取,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • SpringBoot Jpa 自定义查询实现代码详解

    SpringBoot Jpa 自定义查询实现代码详解

    这篇文章主要介绍了SpringBoot Jpa 自定义查询实现代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02

最新评论