Java并发volatile可见性的验证实现

普通读 无法及时获得 主内存变量

public class volatileTest {
  static boolean flag = false;//非volatile变量

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主线程运行完毕");
  }
}

主线程已经修改了flag为true,但子线程一直不会退出循环,因为子线程一直没有同步到 主内存中的变量的值。

截图可见程序一直没有退出,使用dump threads后:

"Thread-0" #12 prio=5 os_prio=0 tid=0x0000000022d89800 nid=0x168 runnable [0x00000000248df000]
java.lang.Thread.State: RUNNABLE
at volatileTest$1.run(volatileTest.java:10)
at java.lang.Thread.run(Thread.java:745)

volatile读 及时获得 主内存变量

public class volatileTest {
  static volatile boolean flag = false;//volatile变量

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主线程运行完毕");
  }
}

加了一个volatile关键字,子线程就能检测到flag的变化了。子线程会退出。

普通读+sleep

public class volatileTest {
  static boolean flag = false;//非volatile变量

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          try {
            Thread.sleep(1);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主线程运行完毕");
  }
}

加了sleep,子线程会退出。

其实就算变量不是volatile的,JVM也会尽量去保证可见性。最开始的例子,由于CPU一直执行循环,没有其他时间来获取 主内存中的变量的最新值,但加了sleep后,CPU就有时间去获取 主内存中的东西了。

普通读+同步块

public class volatileTest {
  static boolean flag = false;//非volatile变量
  static Object sync = new Object();

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          synchronized (sync) {}//随便synchronized一个对象
          //synchronized (this)也可以
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主线程运行完毕");
  }
}

加了同步块,子线程会退出。

这是因为synchronized具体过程是:

  • 获得同步锁;
  • 清空工作内存;
  • 从主内存拷贝对象副本到工作内存;
  • 执行代码(计算或者输出等);
  • 刷新主内存数据;
  • 释放同步锁。

简单的说,synchronized进入时,会将 主内存中最新的变量,拷贝进 自己线程 的工作内存。synchronized退出时,会把 自己线程的工作内存的变量 弄进 主内存中。

同步块 遭遇 锁消除

public class volatileTest {
  static boolean flag = false;//非volatile变量

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          synchronized (new Object()){}
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主线程运行完毕");
  }
}

子线程不会退出。

原因是:synchronized (new Object()){}中这个Object永远不可能逃逸到同步块以外去,所以同步操作其实根本不需要执行了,既然没有执行同步,那么相当于这里是啥也没有。

普通读+System.out.println

public class volatileTest {
  static boolean flag = false;

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          System.out.println("子线程running");
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主线程运行完毕");
  }
}

加了System.out.println,子线程会退出。

因为out这个PrintStream实例的println实现是:

  public void println(String x) {
    synchronized (this) {
      print(x);
      newLine();
    }
  }

因为它也有同步块。

到此这篇关于Java并发volatile可见性的验证实现的文章就介绍到这了,更多相关Java并发volatile验证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java多线程并发编程 Volatile关键字

    volatile 关键字是一个神秘的关键字,也许在 J2EE 上的 JAVA 程序员会了解多一点,但在 Android 上的 JAVA 程序员大多不了解这个关键字.只要稍了解不当就好容易导致一些并发上的错误发生,例如好多人把 volatile 理解成变量的锁.(并不是) volatile 的特性: 具备可见性 保证不同线程对被 volatile 修饰的变量的可见性. 有一被 volatile 修饰的变量 i,在一个线程中修改了此变量 i,对于其他线程来说 i 的修改是立即可见的. 如: vola

  • 深入分析java并发编程中volatile的实现原理

    引言 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的"可见性".可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值.它在某些情况下比synchronized的开销更小,本文将深入分析在硬件层面上Inter处理器是如何实现Volatile的,通过深入分析能帮助我们正确的使用Volatile变量. 术语定义 术语 英文单词 描述 共享变量 在多个线

  • Java并发编程-volatile可见性详解

    前言 要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸.最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍. 有什么用? volatile主要对所修饰的变量提供两个功能 可见性 防止指令重排序 <br>本篇博客主要对volatile可见性进行探讨,以后发表关于指令重排序的博文. 什么是可见性? 把JAVA内存模型(JMM)展示得很详细了,简单概括一下 1.每个Thread有一个属于自己的工作内存(可以理解为每个

  • java 高并发中volatile的实现原理

    java 高并发中volatile的实现原理 摘要: 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的"可见性".可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值.它在某些情况下比synchronized的开销更小 1. 定义: java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量.

  • Java 并发编程:volatile的使用及其原理解析

    Java并发编程系列[未完]: •Java 并发编程:核心理论 •Java并发编程:Synchronized及其实现原理 •Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) •Java 并发编程:线程间的协作(wait/notify/sleep/yield/join) •Java 并发编程:volatile的使用及其原理 一.volatile的作用 在<Java并发编程:核心理论>一文中,我们已经提到过可见性.有序性及原子性问题,通常情况下我们可以通过Synchroniz

  • Java并发编程:volatile关键字详细解析

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用vola

  • Java并发教程之volatile关键字详解

    引言 说到多线程,我觉得我们最重要的是要理解一个临界区概念. 举个例子,一个班上1个女孩子(临界区),49个男孩子(线程),男孩子的目标就是这一个女孩子,就是会有竞争关系(线程安全问题).推广到实际场景,例如对一个数相加或者相减等等情形,因为操作对象就只有一个,在多线程环境下,就会产生线程安全问题.理解临界区概念,我们对多线程问题可以有一个好意识. Jav内存模型(JMM) 谈到多线程就应该了解一下Java内存模型(JMM)的抽象示意图.下图: 线程A和线程B执行的是时候,会去读取共享变量(临界

  • Java并发编程之volatile变量介绍

    volatile提供了弱同步机制,用来确保将变量更新通知到其它线程.volatile变量不会被缓存在寄存器中或者对其它处理器不可见的地方,因此在读取volatile变量时总会返回最新写入的值.可以想象成如下语义,然而volatile是更轻量级的同步机制.volatile只能确保可见性,但不能保证原子性.也就是说不能在复合操作用volatile变量,比如i++. 复制代码 代码如下: public synchronized void setValue(int value){ this.value

  • Java并发volatile可见性的验证实现

    普通读 无法及时获得 主内存变量 public class volatileTest { static boolean flag = false;//非volatile变量 public static void main(String[] args) throws Exception { new Thread(new Runnable() { @Override public void run() { while(!flag){ }; } }).start(); Thread.sleep(100

  • JAVA并发中VOLATILE关键字的神奇之处详解

    并发编程中的三个概念: 1.原子性 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. 2.可见性 对于可见性,Java提供了volatile关键字来保证可见性. 当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值. 而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保

  • Java并发编程之volatile与JMM多线程内存模型

    目录 一.通过程序看现象 二.为什么会产生这种现象(JMM模型)? 三.MESI 缓存一致性协议 一.通过程序看现象 在开始为大家讲解Java 多线程缓存模型之前,我们先看下面的这一段代码.这段代码的逻辑很简单:主线程启动了两个子线程,一个线程1.一个线程2.线程1先执行,sleep睡眠2秒钟之后线程2执行.两个线程使用到了一个共享变量shareFlag,初始值为false.如果shareFlag一直等于false,线程1将一直处于死循环状态,所以我们在线程2中将shareFlag设置为true

  • java并发编程关键字volatile保证可见性不保证原子性详解

    目录 关于可见性 关于指令重排 volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但对于为什么它只能保证可见性,不保证原子性,它又是如何禁用指令重排的,还有很多同学没彻底理解 相信我,坚持看完这篇文章,你将牢牢掌握一个Java核心知识点 先说它的两个作用: 保证变量在内存中对线程的可见性禁用指令重排 每个字都认识,凑在一起就麻了 这两个作用通常很不容易被我们Java开发人员正确.完整地理解,以至于许多同学不能正确地使用volatile 关于可见性 不多bb,码来 public

  • Java并发编程volatile关键字的作用

    日常编程中出现 volatile 关键字的频率并不高,大家可能对 volatile 关键字比较陌生,再深入一点也许是听闻 volatile 只能保证可见性而不能保证原子性,无法有效保证线程安全,于是更加避免使用 volatile ,简简单单加上synchronize关键字就完事了.本文稍微深入探讨 volatile 关键字,分析其作用及对应的使用场景. 并发编程的几个概念简述 首先简单介绍几个与并发编程相关的概念: 可见性 可见性是指变量在线程之间是否可见,JVM 中默认情况下线程之间不具备可见

  • Java并发编程——volatile关键字

    一.volatile是什么 volatile是Java并发编程中重要的一个关键字,被比喻为"轻量级的synchronized",与synchronized不同的是,volatile只能修饰变量,无法修饰方法及代码块等. 下面是使用volatile关键字实现的单例模式: public class Singleton implements Serializable { private static volatile Singleton singleton; private Singleto

随机推荐