volatile 可见性的模拟分析示例

  volatile 作为java的关键字之一,必然有它存在的必要性;在很多的资料中,各位大神级的人物都对volatile做了深入的分析,在这里就不在赘述了;不清的朋友可以迁移到这个地址详细了解:https://www.cnblogs.com/dolphin0520/p/3920373.html

  那么已经了解volatile的作用。这里呢?将使用java代码将把volatile底层 “可见性”给扩大,并已代码的形式展示,volatile的可见性到底是怎么一回事;

  注意:本人亦不清楚volatile的底层是如何实现的,只是,仅仅只是通过各种资料中对volatile的分析,然后领悟出来的想法;(volatile 可能不是如此实现,切莫较真;有大神知道,也请爽快指教)

  最后提示:此文仅供参考,切勿入坑;

  具体实现:

  1,首先模拟主内存,在该模拟的主内存中只存在一个地址,该地址用于存放一个共享变量;代码如下;

import io.netty.util.internal.ConcurrentSet;

public class MainMemory {

    //模拟主内存中的一块内存地址,并存储有一个数据 0
    private int data = 0;

    //记录持有该内存地址数据的所有对象,以便在内存地址数据被改变时,通知这些对象持有的数据无效;
    private ConcurrentSet<ICacheStatus> cacheHolder = new ConcurrentSet<>();

    /**
     * 模拟使用volatile关键字修饰的变量从主内存读取数据:主内存将保持读取者的一个状态修改通知器,当主内存的数据被修改时,会第一时间通知到数据持有者;
     * read方法和write方式使用synchronized关键字修饰,是为了模拟内存地址数据操作的原子性;
     */
    public synchronized int volatileRead(ICacheStatus cache) {
        cache.setStatus(true);
        cacheHolder.add(cache);
        return data;
    }

    /**
     * 模拟非volatile关键字修饰的变量从主内存中读取数据
     */
    public synchronized int read() {
        return data;
    }

    /**
     * 模拟向内存地址中写入数据
     */
    public synchronized void write(int outdata) {
        data = outdata;
        //通知缓存持有者,已持有的数据无效
        for(ICacheStatus holder : cacheHolder) {
            holder.setStatus(false);
        }
    }

    /**
     * 模拟缓存持有者释放缓存,主内存将在以后的数据改变时,不通知改对象;
     * @param cacheHolder
     */
    public void releaseCache(ICacheStatus outcacheHolder) {
        cacheHolder.remove(outcacheHolder);
    }

}

  2, 缓存状态通知器接口,代码如下:

public interface ICacheStatus {

    void setStatus(boolean status);
}

  3,模拟线程本地缓存对象,该对象针对于变量是否被volatile变量修饰,提供两种不同的操作:1,volatile修饰的变量,在使用时会检查本地缓存的数据是否过期,如过期,则向主内存重新获取; 2,非volatile修饰的变量,不检查是否过期(当然,这不合理,sun也应该不是这么实现的,仅供模拟   “volatile可见性”的演示;重要的事说n遍);代码如下:public class ThreadLocalCache implements ICacheStatus     //本地缓存从主内存中读取到的数    private int cache = -1;    /*

     * 模拟当前缓存数据的状态,true可用 ,false为不可用:需要向主内存中再次读取数据;
     */
    private boolean cacheFlag;

    //主内存
    private MainMemory mainMemory;

    public  ThreadLocalCache(MainMemory mm) {
        this.mainMemory = mm;     this.cache = mm.volatileRead(this);
    }

    @Override
    public void setStatus(boolean status) {
        this.cacheFlag = status;
    }

    //模拟变量被volatile关键字修饰,在使用前会检查当期缓存的数据是否过期,如果过期则向主内存从新读取;
    public int volatileRead() {
        if(!cacheFlag)
            cache = mainMemory.volatileRead(this);
        return cache;
    }

    //模拟非volatile变量的使用;
    public int read() {
        return cache;
    }

    public void write(int data) {
        mainMemory.write(data);
    }

    /*
     * 模拟gc释放资源
     */
    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        mainMemory.releaseCache(this);
    }
}

  4, 测试:模拟可见性的影响

  

    static public void main(String[] args) {

        //模拟一个主内存,并且在该主内存中存在一个共享变量
        final MainMemory mainMemory = new MainMemory();

        //1.0 模拟 《使用volatile关键字修饰的变量的方式读写数据时,volatile可见性的体现以及对共享变量的影响》
        //1.1模拟创建2个线程的 缓存
        ThreadLocalCache threadCache1 = new ThreadLocalCache(mainMemory);
        ThreadLocalCache threadCache2 = new ThreadLocalCache(mainMemory);

        //1.2 模拟两个2线程同时读取了一个volatile关键字修饰变量的值;
        int d1 = threadCache1.volatileRead();
        int d2 = threadCache2.volatileRead();
        System.out.println("独立的线程缓存获取到的数据:"  + " ;d1 = " + d1 + " ;d2 = " + d2);

        //1.3模拟线程1向主内存中写入了数据,并打印主内存的值;
        threadCache1.write(threadCache1.volatileRead() + 1);
        System.out.println("当线程1修改主内存后,主内存中的值 = " + mainMemory.read());

        // 注意: 这里将模拟volatile可见性,以及可见性对其它线程的影响;
        //1.4模拟线程2使用共享变量,并在该共享变量上加1;
        //在使用volatile修饰的共享变量时,会检查当前线程缓存中的值是否可用,否则向 主内存中重新读取;
        //这里线程1在之前已修改了主内存的值,所以线程2值已被通知不可用,线程2向主内存重新读取最新值;
        threadCache2.write(threadCache2.volatileRead() + 1);
        System.out.println("当线程1修改主内存后,主内存中的值 = " + mainMemory.read());

        //非volatile变量发生多个线程同时读写,修改值预期不一致演示

        //模拟一个主内存,并且在该主内存中存在一个共享变量
        final MainMemory mainMemory2 = new MainMemory();

        //2.0 模拟 《使用非volatile关键字修饰变量的方式读写数据时,对共享变量的影响》
        //1.1模拟创建2个线程的 缓存
        ThreadLocalCache threadCache3 = new ThreadLocalCache(mainMemory2);
        ThreadLocalCache threadCache4 = new ThreadLocalCache(mainMemory2);

        //1.2 模拟两个2线程同时读取了一个非volatile关键字修饰的共享变量的值;
        int d3 = threadCache3.read();
        int d4 = threadCache4.read();
        System.out.println("独立的线程缓存获取到的数据: "+ " ;d3 = " + d3 + " ;d4 = " + d4);

        //1.3模拟线程3向主内存中写入了数据,并打印主内存的值;
        threadCache3.write(threadCache3.read() + 1);
        System.out.println("当线程3修改主内存后,主内存中的值 = " + mainMemory2.read());

        //注意 : 这里将模拟使用非volatile变量,在线程缓存中的操作,因为当前缓存不知道数据已过期,并将已过期的数据
        //拿来使用,造成最后得到的值,与预期值不同;
        //1.4模拟线程4向主内存中写入了数据,并打印主内存的值;
        threadCache4.write(threadCache4.read() + 1);
        System.out.println("当线程4修改主内存后,主内存中的值 = " + mainMemory2.read());

    }
    

  

原文地址:https://www.cnblogs.com/loveyoumi/p/9463749.html

时间: 2024-08-05 03:02:40

volatile 可见性的模拟分析示例的相关文章

volatile可见性的一些认识和论证

一.前言 volatile的关键词的使用在JVM内存模型中已是老生常谈了,这篇文章主要结合自己对可见性的一些认识和一些直观的例子来谈谈volatile.文章正文大致分为三部分,首先会介绍一下happen-before,接着讲解volatile的一些使用场景,最后会附上一些例子来论证使用与不使用volatile的区别. 二.happen-before 对操作系统有认识的同学一定知道,CPU一般有三级缓存,在与内存交互的时候,存在缓存与内存的更新问题,其次CPU在读取指令的时候,会做一些指令重排序的

Android中app卡顿原因分析示例

在知乎回答了一个“为什么微博的app在iPhone比Android上流畅”的问题.后面部分是一个典型的动画卡顿的性能分析过程,因此帖在这里.有编程问题可以在这里交流.知乎链接. ========================================================= 我来说下我所知道的事情.我不知道iOS为什么流畅,但我知道一些Android为什么不流畅的原因. 首先,就题主所说的问题,我用iPad和小米Pad对比了一下微博滑动滚屏这件事情(2014年8月10日目前微博

Tecplot Chorus 2015 R2 Win64 &amp; Linux64 2CD CFD模拟分析

Tecplot Chorus 2015 R2 Win64 & Linux64 2CD CFD模拟分析Tecplot Chorus为工程师分析大量模拟数据提供了一个崭新的工具.Tecplot Chorus集成了元数据分析.后处理和模拟数据管理功能,方便 于加快工程决策的制定.在CFD研究中,Tecplot Chorus使工程师能更加简单地发现趋势和异常情况,同时洞察引起这些变化的潜在的流动现象.通过帮助工程师协作分析大量模拟数据,Tecplot Chorus将会有助于工程师更快地获得更加有信息的结

著名Invensys SimSci-Esscor PIPEPHASE 9.5 石油化工模拟分析

著名Invensys SimSci-Esscor PIPEPHASE 9.5 石油化工模拟分析 这是管网模拟计算分析软件PipePhase PipePhase模拟分析软件,应用在油气加工.精细化工.制药等领域:完成设计.装置改造.优化和改进装置产量和效益.软件提供模型有:一般化闪蒸.精馏.热器.反应 器.聚合物.固体和多种热力学模型供用户选择PipePhase [/b]应用于油气管道网络和管道系统中,是精确模拟稳态多相流程的程序.在一个单井中,该软件能适应从主要参数的灵敏度分析中模拟应用并广泛地

TECPLOT.CHORUS.2015.R2.WIN.LINUX.X64 CFD模拟分析

TECPLOT.CHORUS.2015.R2.WIN.LINUX.X64 CFD模拟分析Tecplot  Chorus为工程师分析大量模拟数据提供了一个崭新的工具.Tecplot Chorus集成了元数据分析.后处理和模拟数据管理功能,方便 于加快工程决策的制定.在CFD研究中,Tecplot Chorus使工程师能更加简单地发现趋势和异常情况,同时洞察引起这些变化的潜在的流动现象.通过帮助工程师协作分析大量模拟数据,Tecplot Chorus将会有助于工程师更快地获得更加有信息的结果. Si

Java并发编程-volatile可见性的介绍

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

volatile可见性案例-黑马

volatile可见性案例-黑马 package com.mozq.demo.demo; class Task implements Runnable{ //public boolean flag = false;//存在可见性问题,主线程无法监视到线程执行成功. public volatile boolean flag = false; @Override public void run() { if(!flag){ System.out.println("执行任务"); } fla

ArcGIS教程:地统计模拟的示例

模拟示例 示例 I 在世界上的许多城市和地区,空气质量都是令人关注的重要健康指标之一.在美国,众所周知,洛杉矶的空气质量不是很好,分布密集的监控网络每半天就对臭氧.微粒物质和其他污染物等数据进行一次收集.基于此空气质量数据,可获得每种污染物的浓度以及污染物每年超过州空气质量标准和联邦空气质量标准的天数.由于这两个测量值均支持对在某个特定区域内生活进行感染风险的局部评估,因此,每年超过临界阈值的天数可用来建立显示超过阈值概率的内插地图. 在本示例中,对 2005 年加利福尼亚州每个监测站臭氧超过阈

javaCore分析示例(转)

当两个或多个线程彼此形成循环依赖关系时,就出现了死锁.例如,如果线程 A 处于等待线程 B 的等待状态,而同时线程 B 处于等待线程 A 的等待状态,则出现了死锁.一旦形成此情况,线程 A 和线程 B 都不能有任何进展,因为这两个线程现在都无限期地挂起了.为什么会有人创建这种系统?当然,您并不会有意这么做,但由于存在大量线程和复杂事务,因此很容易出现这种情况. 本文将介绍如何使用 IBM WebSphere Application Server V6.1 的线程转储工具来对系统进行检查,以确定是