当前位置: 首页 > news >正文

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

相关文章:

  • 管理微服务中的数据
  • bzoj 2002 弹飞绵羊 lct裸题
  • 【c学习-3】
  • BZOJ 4589 Hard Nim
  • MongoDB学习笔记Day3
  • teamview被限制使用的解决办法
  • 随机数据构造-Faker
  • mybaits出现错误
  • 如何恢复u盘误删文件,看完就不会觉得自己很菜了
  • ubuntu 查看apt-get有哪些软件
  • 用Visual Studio开发以太坊智能合约
  • 搭载AI引擎,腾讯云云镜开启全面防护模式
  • 学习日记0821组合 多态 封装
  • 基于名字自动发布之数据库(4)
  • 洛谷P2526 [SHOI2001]小狗散步(二分图匹配)
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • PAT A1120
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 编写高质量JavaScript代码之并发
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 技术发展面试
  • 老板让我十分钟上手nx-admin
  • 利用jquery编写加法运算验证码
  • 配置 PM2 实现代码自动发布
  • 七牛云假注销小指南
  • 前端攻城师
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 硬币翻转问题,区间操作
  • 由插件封装引出的一丢丢思考
  • Hibernate主键生成策略及选择
  • # Apache SeaTunnel 究竟是什么?
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • (42)STM32——LCD显示屏实验笔记
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (轉)JSON.stringify 语法实例讲解
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET Core 项目指定SDK版本
  • @ConditionalOnProperty注解使用说明
  • @NestedConfigurationProperty 注解用法
  • @Validated和@Valid校验参数区别
  • [ Linux ] Linux信号概述 信号的产生
  • [C++]类和对象【下】
  • [C++]拼图游戏
  • [Flex] PopUpButton系列 —— 控制弹出菜单的透明度、可用、可选择状态
  • [Godot] 3D拾取
  • [IE编程] IE中对网页进行截图的编程接口
  • [iOS]如何删除工程里面用cocoapods导入的第三方库
  • [LeetCode] NO. 169 Majority Element
  • [Mybatis-Plus笔记] MybatisPlus-03-QueryWrapper条件构造器
  • [mysql]错误解决之Failed to start MySQL Server
  • [nlp] grad norm先降后升再降