博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
volatile 可见性的模拟分析示例
阅读量:7080 次
发布时间:2019-06-28

本文共 4886 字,大约阅读时间需要 16 分钟。

  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
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

你可能感兴趣的文章
Configuring Oracle Data Integrator for Cloudera
查看>>
Varnish缓存部署方式及原理详解
查看>>
C语言字符串拷贝strcpy函数的陷阱分析
查看>>
KVM虚拟化开源高可用方案(四)sheepdog
查看>>
排错之网络映射缓存凭证记录导致备份计划任务失败
查看>>
vSphere Data Protection 6.1.2部署与配置
查看>>
PfSense基于BSD的软件防火墙的安装、配置与应用
查看>>
使用shell和expect一键批量分发SSH密钥脚本
查看>>
ESXI上的vm虚拟机文件被锁定无法POWER ON的问题处理
查看>>
《统一沟通-微软-培训》-2-部署-反向代理-2-配置初始的部署设置
查看>>
Exchange Server 2013部署系列之五:在Windows8上部署管理工具
查看>>
“3.15”曝二维码安全漏洞 瑞星呼吁重视WiFi安全
查看>>
关于团队建设的访谈
查看>>
Openstack:一根中国稻草-【中国IC微专栏】2016.4.28
查看>>
Lync 小技巧-51-Lync 2013-不加域-客户端-1-下载-证书-信任链
查看>>
安装Exchange Server 2013
查看>>
Win7部署基础知识(1):部署的基本概念与方法论
查看>>
州小吃”小程序正式发布,酷客多全程技术支持
查看>>
理解思科IPS系统的virtual sensor
查看>>
MySQL5.6创建用户密码不再明文显示在binlog二进制日志文件里
查看>>