关于volatile的可见性和禁止指令重排序的疑惑

在学习volatile语义的可见性和禁止指令重排序的相关测试中,发现并不能体现出禁止指令重排序的特性

实验代码如下

package com.aaron.beginner.multithread.volatiletest;

import java.util.concurrent.CountDownLatch;

/**
 * @author
 * @description 一句话描述该文件的用途
 * @date 2017-03-01
 */
public class VolatileAndNonVolatileTest
{

    private volatile boolean flag;

    private int num;

    public static void main(String[] args) throws InterruptedException
    {

        for (int i = 0; i < 5000; i++)
        {
            final VolatileAndNonVolatileTest test = new VolatileAndNonVolatileTest();

            CountDownLatch latch = new CountDownLatch(2);
            Thread write = new Thread(() ->
            {
                test.write();
                latch.countDown();
            });

            Thread read = new Thread(() ->
            {
                test.read();
                latch.countDown();
            });

            write.start();
            read.start();

            latch.await();
        }
    }

    private void read()
    {
        if (flag)
        {
            System.out.println("=" + num * num);
        }
    }

    private void write()
    {
        num = 10;//步骤1

        flag = true;//步骤2

    }

}

实验步骤:

注意关注代码中flag变量,会分别测试flag变量有volatile修饰和没有volatile修饰时的输出情况

本测试代码主要的功能:

  1. write方法负责修改num变量,修改后设置flag=true,表示num变量已经修改成功
  2. read方法负责读取修改后的num值,并输入平方

实验步骤:

  1. 在循环中每次启动两个线程,一个线程负责调用write修改数据,另一个线程负责读取修改后的值
  2. 观察实验结构

预期结果:

  1. 当flag没有volatile修饰的时候,我们本应该期望的是能够输出"=100"
  2. 当flag有volatile修饰的时候,由于有可见性和禁止了指令重排序,每次正常输出"=100"

实际结果:

  1. 当flag没有volatile修饰的时候,多次实验均没有发现指令重排序发生(即出现"=0",也就是说write方法中步骤1和步骤2没有发生重排序(步骤2比步骤1先发生))
  2. 当flag有volatile修饰的时候(理论上有可见性和禁止了指令重排序,不会出现"=0"),实际上也没有出现"=0",和1结果一致。

  即加了和没加volatile的结果是一致的

试问各位大神,有知道怎么测试这个volatile,体现禁止了指令重排序的特点和可见性的特点吗?

时间: 2024-11-08 22:01:02

关于volatile的可见性和禁止指令重排序的疑惑的相关文章

volatile为什么可以保证内存可见性及防止指令重排序?

内存 共享主存和高速缓存(工作内存).CPU高速缓存(L1,2)产生原因读写主存没有CPU执行指令快,他是某个CPU独有,只与该CPU运行的线程有关. 内存可见性 简单的说,CPU对数据的修改,对其他CPU立刻可见.下面我们详细地说. CPU修改数据,首先对工作内存修改,再同步主内存.单线程中,变量在工作内存的副本一直有效,CPU不用每次修改从主存读取变量,只是每次修改后同步主存. 对其他CPU立刻可见.当一个CPU修改变量,同步主存,如果其他CPU的工作内存也缓存这个变量,那么这个CPU的变量

Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专家又往往建议我们远离它.比如Thread这个很基础的类,其中很重要的线程状态字段,就是用volatile来修饰,见代码 /* Java thread status for tools, * initialized to indicate thread 'not yet started' */   p

J.U.C JMM. pipeline.指令重排序,happen-before(续)

前面已经介绍硬件平台Cache Coherence问题和解决办法,下面来看看Java虚拟机平台的相关知识.硬件平台处理器,高速缓存,主存之间的交互关系如下: Java内存模型(JMM)        Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model, JMM)来屏蔽掉底层各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果. 主内存和工作内存       Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟

轻量级的同步机制——volatile语义详解(可见性保证+禁止指令重排)

1.关于volatile volatile是java语言中的关键字,用来修饰会被多线程访问的共享变量,是JVM提供的轻量级的同步机制,相比同步代码块或者重入锁有更好的性能.它主要有两重语义,一是保证多个线程对共享变量访问的可见性,二防止指令重排序. 2.语义一:内存可见性 2.1 一个例子 public class TestVolatile { public static void main(String[] args) throws InterruptedException { ThreadD

JVM并发机制的探讨——内存模型、内存可见性和指令重排序

[转]http://my.oschina.net/chihz/blog/58035 文章写的非常好,为作者点赞. JAVA内存模型 对于我们平时开发的业务应用来说,内存应该是访问速度最快的存储设备,对于频繁访问的数据,我们总是习惯把它们放到内存缓存中,有句话不是说么,缓存就像是清凉油,哪里有问题就抹一抹.但是CPU的运算速度比起内存的访问速度还要快几个量级,为了平衡这个差距,于是就专门为CPU引入了高速缓存,频繁使用的数据放到高速缓存当中,CPU在使用这些数据进行运算的时候就不必再去访问内存.但

多线程之指令重排序

为什么会乱序 现在的CPU一般采用流水线来执行指令.一个指令的执行被分成:取指.译码.访存.执行.写回.等若干个阶段.然后,多条指令可以同时存在于流水线中,同时被执行. 指令流水线并不是串行的,并不会因为一个耗时很长的指令在"执行"阶段呆很长时间,而导致后续的指令都卡在"执行"之前的阶段上. 相反,流水线是并行的,多个指令可以同时处于同一个阶段,只要CPU内部相应的处理部件未被占满即可.比如说CPU有一个加法器和一个除法器,那么一条加法指令和一条除法指令就可能同时处

深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则[转]

在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的. 显然只有资源竞争时才会导致线程不安全,因此无状态对象永远是线程安全的. 原子操作的描述是: 多个线程执行一个操作时,其

深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安

java并发学习-第九章 指令重排序

一.happns-before happns-before是学习指令重排序前的一个必须了解的知识点,他的作用主要是就是用来判断代码的执行顺序. 1.定义 happens-before是用来指定两个操作之间的执行顺序.提供跨线程的内存可见性. 在java内存模型中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必然存在happens-before关系 2.happens-before规则 a.程序顺序规则 单线程中的每个操作,总是前一个操作happens-before于该线程中的任