Java并发编程中volatile实现过程详细解析

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

  首先并发编程有三大特性: 可见性,有序性,原子性。volatile关键字实现了前面两个特性。那么它是如何实现这两个特性的呢?

  首先是可见性。可见性主要是让缓存,直接写穿透到主存中。然后另外的cpu 通过底层的硬件层面的嗅探,可以发现自己cpu本地的缓存已经失效。然后到主存中直接读取。现在让我们来看看,cpu里面的缓存具体和主存如何交互。

  说道这里首先需要了解,计算机内部存储器结构。每个核中都有自己的通用寄存器,比如eax,ebx,edx,esi,esp.访问这些寄存器里面的内容,只要一个机器周期,就够了,通常小于1ns..然后是L1,L2的本地core的缓存。 通常在10个机器周期左右。大约10ns. L3级缓存是多core共享的。

      以我们常见的X86芯片为例,Cache的结构下图所示:整个Cache被分为S个组,每个组是又由E行个最小的存储单元——Cache Line所组成,而一个Cache Line中有B(B=64)个字节用来存储数据,即每个Cache Line能存储64个字节的数据,每个Cache Line又额外包含一个有效位(valid bit)、t个标记位(tag bit),其中valid bit用来表示该缓存行是否有效;tag bit用来协助寻址,唯一标识存储在CacheLine中的块;而Cache Line里的64个字节其实是对应内存地址中的数据拷贝。根据Cache的结构题,我们可以推算出每一级Cache的大小为B×E×S。   

1级大概是32k 或者32K X2  ,2级大概是 256K或者256KX2 ,L3一般是3M左右。当多线程并发访问一段代码的时候,读取变量到本地的core进行计算,然后把数据写入到缓存中,假如没有volatile关键字的话,缓存采用的是write back 策略,直接写到缓存,看如下代码,虽然启用了10个线程进行计数,但是打印出来的count值是0.即使sleep(100),100ms,等所有线程都起来了,也是得到的结果都是不定的,因为无法确定缓存什么时候,换出写到主存中。

1 public class VolatileTest {
2
3     private int count ;
4     public void increase() {
5         count++;
6     }
7     public void  getCount(){
8         System.out.println(count);
9     }
10     public static void main(String[] args) throws InterruptedException{
11         VolatileTest test =  new VolatileTest();
12         for(int i=0;i<10;i++){
13             new Thread(){
14                 @Override
15                 public void run() {
16                     for(int j=0;j<1000;j++)
17                         test.increase();
18                 }
19             }.start();
20         }
21         Thread.sleep(100);
22         test.getCount();
23     }
24 }

     volatile 作用1 就是一个线程改变了共享变量的值,其它线程马上能看见,就是可见性。比如下面的这段代码。

 1 import java.util.concurrent.CountDownLatch; 2  3 public class VolatileTest2 { 4  5     private static volatile boolean status=false ; 6  7     private static CountDownLatch start  = new CountDownLatch(1); 8  9     public void setStatusTrue(){10         status =true;11     }12     public void  getStatus(){13         System.out.println(status);14     }15     public static void main(String[] args) throws InterruptedException{16         VolatileTest2 test =  new VolatileTest2();17         new Thread(new Task2(start,test)).start();18         for(int i=0;i<10;i++){19             new Thread(new Task1(start,test)).start();20         }21     }22 }23 24 class Task1 implements Runnable{25     private CountDownLatch latch;26     private VolatileTest2 test ;27     public Task1(CountDownLatch start,VolatileTest2 test){28         this.latch = start;29         this.test = test;30     }31     @Override32     public void run() {33         try{34            latch.await();35         }catch (Exception e){36         }37             test.getStatus();38     }39 }40 41 /**42  * 这个线程吧状态设置成true,然后同步计数器马上变成0.之后,就其它线程马上就能看到status状态为true43  */44 class Task2 implements Runnable{45     private CountDownLatch latch;46     private VolatileTest2 test ;47     public Task2(CountDownLatch start,VolatileTest2 test){48         this.latch = start;49         this.test = test;50     }51     @Override52     public void run() {53         test.setStatusTrue();54         latch.countDown();55         System.out.println("countDown===");56     }57 }

       具体的原理,这里涉及到缓存一致性原理,MESI 协议

       失效(Invalid)缓存段,要么已经不在缓存中,要么它的内容已经过时。为了达到缓存的目的,这种状态的段将会被忽略。一旦缓存段被标记为失效,那效果就等同于它从来没被加载到缓存中。

       共享(Shared)缓存段,它是和主内存内容保持一致的一份拷贝,在这种状态下的缓存段只能被读取,不能被写入。多组缓存可以同时拥有针对同一内存地址的共享缓存段,这就是名称的由来。

       独占(Exclusive)缓存段,和S状态一样,也是和主内存内容保持一致的一份拷贝。区别在于,如果一个处理器持有了某个E状态的缓存段,那其他处理器就不能同时持有它,所以叫“独占”。这意味着,如果其他处理器原本也持有同一缓存段,那么它会马上变成“失效”状态。

       已修改(Modified)缓存段,属于脏段,它们已经被所属的处理器修改了。如果一个段处于已修改状态,那么它在其他处理器缓存中的拷贝马上会变成失效状态,这个规律和E状态一样。此外,已修改缓存段如果被丢弃或标记为失效,那么先要把它的内容回写到内存中——这和回写模式下常规的脏段处理方式一样。

       在写入时锁定缓存,称为Exclusive状态,然后同时写入缓存和主存,当读取数据的时候,强行,从主存中读取,并且申请缓存行填充。

2 有序性,这个又如何保证呢?

       《深入理解Java虚拟机》中有这句话“”“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”“”,lock前缀指令实际上相当于一个内存屏障(也成内存栅栏)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;至于什么是内存屏障,不做深入了解。只需要知道是CPU Out-of-order execution 和 compiler reordering optimizations。用于对内存操作的顺序限制。

Memory access instructions, such as loads and stores, typically take longer to execute than other instructions. Therefore, compilers use registers to hold frequently used values and processors use high speed caches to hold the most frequently used memory locations. Another common optimization is for compilers and processors to rearrange the order that instructions are executed so that the processor does not have to wait for memory accesses to complete. This can result in memory being accessed in a different order than specified in the source code. While this typically will not cause a problem in a single thread of execution, it can cause a problem if the location can also be accessed from another processor or device.

转载于:https://my.oschina.net/u/3980693/blog/2967178

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《ASP.NET Core 6框架揭秘》实例演示[32]:错误页面的N种呈现方式

由于ASP.NET是一个同时处理多个请求的Web应用框架&#xff0c;所以在处理某个请求过程中出现异常并不会导致整个应用的中止。出于安全方面的考量&#xff0c;为了避免敏感信息外泄&#xff0c;客户端在默认情况下并不会得到详细的出错信息&#xff0c;这无疑会在开发过程中增加…

Golang并发模型:合理退出并发协程

goroutine作为Golang并发的核心&#xff0c;我们不仅要关注它们的创建和管理&#xff0c;当然还要关注如何合理的退出这些协程&#xff0c;不&#xff08;合理&#xff09;退出不然可能会造成阻塞、panic、程序行为异常、数据结果不正确等问题。这篇文章介绍&#xff0c;如何合…

关于8位AD_DA转换芯片的采样率问题

关于使用Keil计算程序执行时间 打开Keil程序&#xff0c;进入“启动/停止调试”界面。在需要暂停的地方设置断点&#xff08;在该句程序前双击&#xff09;。在程序上方有一行工具栏&#xff1a;此工具栏分别代表复位、运行、停止、步进、步越、步出、运行到光标处等。 点击运…

CYQ.Data 数据框架 V4.0 开源版本发布(源码提供下载,秋色园V2.5版本标配框架)

说明的说明&#xff1a; 博客园团队两次移此文出首页&#xff0c;说 这篇文章不属于知识分享型文章&#xff0c;并且有广告嫌疑。 本文的确属于分享型文章&#xff0c;而且分享的知识点比其它文章都多很多&#xff0c;看看网友回复“谢谢分享”就知道是分享型文章了。 所谓广告…

oracle 分组后取每组第一条数据

数据格式 分组取第一条的效果 sql SELECT * FROM (SELECT ROW_NUMBER() OVER(PARTITION BY x ORDER BY y DESC) rn, test1.* FROM test1) WHERE rn 1 ;

树莓派Zero 2 W(ubuntu-22.04)通过.NET6和libusb操作USB读写

有这个想法的初衷喜欢电子和DIY硬件的朋友对稚晖君应该都不陌生&#xff0c;他定期都会分享一些自己做的好玩的硬件&#xff0c;他之前做了一个ElectronBot桌面机器人我就很感兴趣&#xff0c;所以就自己也做了一个。起初我只是自己开发了一个叫电子脑壳的上位机软件&#xff0…

bzoj4589

fwt 原理并不知道 nim游戏石子异或和0后手赢 那么也就是求a[1]^a[2]^...^a[n]0的方案数 这个和bzoj3992一样可以dp dp[i][j]表示前i个数异或和为j的方案数 dp[0][0] 1 dp[i][j] dp[i - 1][k] * a[p] p ^ k j a[p] 0 / 1 表示有没有p这个数 这个东西也不能矩阵快速幂 但是我…

web框架之Django(一)

Python的WEB框架有Django、Tornado、Flask 等多种&#xff0c;Django相较与其他WEB框架其优势为&#xff1a;大而全&#xff0c;框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。 基本配置 一、创建django程序 终端命令&#xff1a;django-admin startprojec…

程序员制作出价值5亿外卖神器却不能取消订单,你知道吗?

小编今日给大家带来RACDisopsable&#xff0c;大家可能有部分人对这个会感觉到很陌生&#xff0c;那么我就用一句话来表达就是他可以帮我们取消订阅。那么又会有人会对这个产生疑问了&#xff0c;我们什么时候需要用到这个取消订阅了打个实际的例子来说吧&#xff0c;今天我在饿…

《精读 Mastering ABP Framework》教程发布

精读《Mastering ABP Framework》学习总结&#xff0c;掌握软件开发最佳实践&#xff0c;构建可维护 .NET 解决方案。从 ABP Framework 框架中学习如何构建现代 WEB 应用程序。掌握 ABP Framework 框架ABP Framework 是一个完整的基础架构&#xff0c;遵循软件开发最佳实践&…

C# 委托知识总结

1.什么是委托&#xff0c;为什么要使用委托 我正在埋头苦写程序&#xff0c;突然想喝水&#xff0c;但是又不想自己去掉杯水而打断自己的思路&#xff0c;于是我就想让女朋友去给我倒水。她去给我倒水&#xff0c;首先我得让她知道我想让她干什么&#xff0c;通知她之后我可以继…

阿里云大学课程学习有奖征文活动现在开始

2019独角兽企业重金招聘Python工程师标准>>> "学有所获&#xff0c;分享为美"--阿里云大学课程学习有奖征文活动开始啦~~ 看课程&#xff0c;写心得&#xff0c;赢千元大奖&#xff0c;还有机会加入阿里云大学技术作者群&#xff01;想试试自己的技术文笔…

Android 的系统架构

Android 的系统架构和其它操作系统一样&#xff0c;采用了分层的架构。android 分为四个层&#xff0c;从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和 linux 核心层。 Android 是以 Linux 为核心的手机操作平台&#xff0c;作为一款开放式的操作系统&#xf…

记一次 .NET 某制造业 MES 系统崩溃分析

一&#xff1a;背景 1.讲故事前段时间有位朋友微信找到我&#xff0c;说他的程序偶尔会出现内存溢出崩溃&#xff0c;让我帮忙看下是怎么回事&#xff0c;咨询了下程序是 x86 部署&#xff0c;听到这个词其实心里已经有了数&#xff0c;不管怎么样还是用 windbg 分析一下。二&a…

首次公开!单日600PB的计算力--阿里巴巴EB级大数据平台的进击

摘要&#xff1a; 每年的双11之前&#xff0c;也是MaxCompute各种乾坤大挪移落定的时候&#xff0c;因为双11就是各种大折腾项目的自然deadline。在今年双11之前&#xff0c;一路向北迁移和在离线混部项目&#xff0c;将杭州集群除蚂蚁外整体迁移到张北&#xff0c;涉及了绝大部…

软件测试金字塔

软件测试金字塔 在敏捷方法中&#xff0c;持续集成是其基石&#xff0c;持续集成的核心是自动化测试。下面这篇关于测试金字塔的文章&#xff0c;来自大师Martin Fowler。 测试金字塔的概念来自Mike Cohn&#xff0c;在他的书Succeeding With Agile中有详细描述&#xff1a;测试…

使用pm2守护你的.NET Core应用程序

简介PM2是常用的node进程管理工具&#xff0c;它可以提供node.js应用管理&#xff0c;如自动重载、性能监控、负载均衡等。同类工具有Supervisor、Forever等。pm2是一个进程管理工具,可以用它来管理你的node进程&#xff0c;并查看node进程的状态&#xff0c;当然也支持性能监控…

程序员搞笑段子

转载于:https://www.cnblogs.com/Zhusi/p/10083474.html

CA周记 - 在 Azure ML 上用 .NET 跑机器学习

.NET 是一个跨平台&#xff0c;全场景应用的开源技术。你有在用 .NET 做机器学习/深度学习的应用吗&#xff1f;如果从框架角度&#xff0c;ML.NET / Tensorflow.NET / 不断在进步的 TorchSharp 通过几年的发展已经开始稳定&#xff0c;但如果在一些大型项目上&#xff0c;特别…

iOS10 优化APP首次安装网络权限提示方案

我刚经历了一场末日&#xff08;停电&#xff09;&#xff0c;特别是在你想写文档的时候。。。 言归正传&#xff0c;今天的问题是解决iOS10系统下首次按钮APP弹出的网络权限提示所带来了问题以及优化。 起因 查了相关文章知道由于大陆工信部出台的新规指出&#xff0c;应用在未…