java硬件编程_关于JAVA并发编程你需要知道的——硬件篇

无论程序语言如何千变万化,他们都深深地根植于目前的计算机体系结构。

左图是intel CPU的三级高速缓存设计,由于高速缓存对程序员基本不可见,因此可以抽象为右图。

9531d4dcc475a128db952d181f7c3cd4.png

缓存的设计

首先还是先谈谈左图。

L1-cache分为两部分,i-cache存储指令(只读),d-cache存储数据(可读可写)

CPU只能和寄存器以及L1-cache进行直接交互,数据不能隔层传递,只能一层一层往上读,一层一层往下写

访问L1需要至少4个时钟周期,L2需要至少10个,L3需要至少30个。即便是速度最快的L1,也低于运算单元的执行速度,何况存在缓存未命中的情况,因此在L1和运算单元之间加上了Writebuffer和Readbuffer(合称Memory Ordering Buffer,MOB),数据准备好的时候再完成相关指令,这就是CPU指令乱序——顺序执行乱序完成。乱序完成的结果放入到Writebuffer中,按照原有的执行顺序,刷到缓存中

缓存由多个缓存行组成。每个缓存行结构如图所示(以64位机器为例)

4df52fc7218fcd15d484462c5458d40c.png

CPU读取缓存的时候找到对应的缓存行,如果前面的有效位为零,就从下一级缓存加载到这一级缓存

相关的问题

明白缓存的设计之后,再看右图来分析其中的问题

缓存导致的内存可见性:已知线程A运行在core 0上,线程B运行在core 1上,两者都对同一个内存地址进行读取,这个内存地址的内容会被加载到cache,然后CPU读取,这时候线程A对内容进行了修改,但是线程B却可能一直从本核心的cache读取,无法感知到该地址的内容已被修改。

多核导致的自增操作原子性:自增操作分为三步:从内存读取变量到寄存器;寄存器中的值加1;写回到内存。已知线程A运行在core 0上,线程B运行在core 1上,两者都对变量执行加一操作。A执行完一二两步时,B执行完第一步,A将加一后的值写入到内存,B执行完二三两步也将加一后的值写入到内存,结果变量只加了一,而不是加二

MOB导致的cache可见性:a=1.0; a=a/2; a=a-1.0;按照正常的逻辑,a最后的结果为-0.5;但是因为除法的执行时钟周期大于减法,第三句执行时,a/2的结果存放在writebuffer中还没写入到缓存,a-1.0中a的值已经从缓存中加载到readbuffer,也就是a-1.0=1.0-1.0=0 (高级语言不会出现这个问题,因为编译器已经做了处理,前面的伪代码仅表示逻辑)

相关的实现

为了解决这些问题,CPU提供了一些指令,其中比如lock和cmpxchg。

lock 汇编前缀,在Intel奔腾系列之前,这个指令前缀能够锁定总线,禁止其他CPU核心操作内存,执行完后边的指令后释放总线,在这个过程中其他CPU核心会监听总线,发现某个内存地址内容被修改,就会将本核心下的对应cache行有效位置0。因为锁总线会禁止所有内存操作,降低效率,因此在奔腾之后,这个指令前缀不锁总线而是锁定相关的cache行,对某个地址修改后直接让相关cache失效。这样解决了问题一

cmpxchg 将寄存器a中的值与内存中比较如果一样,将寄存器c中的值和内存中的值交换,如果不一样就设置异常位并将内存中的值读取到寄存器a。代入到问题二,从内存读取值到寄存器a,加一后保存到寄存器c,然后执行cmpxchg,执行完后如果有异常,就重新加一,再尝试写回,直到成功。这样解决了问题二

cmpxchg和lock 在执行完以后会将writebuffer刷到cache并清空readbuffer,这样解决了问题三。另外X86_64引入了内存屏障指令 lfence、sfence、mfence。lfence前面的读取操作完成,也就是readbuffer中的内容全部被cpu读取后,才能执行lfence之后的读取操作;sfence前面的写入操作完成,也就是writebuffer全部刷到缓存中,才能执行sfence之后的写入操作;mfence之前的读写操作全部完成,才能进行mfence之后的操作。

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

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

相关文章

ELF 动态链接 so的动态符号表(.dynsym)

静态链接中有一个专门的段叫符号表 -- “.symtab”(Symbol Table), 里面保存了所有关于该目标文件的符号的定义和引用。 动态链接中同样有一个段叫 动态符号表 -- “.dynsym”(Dynamic Symbol) , 但.dynsym 相对于 .symtab 只保存了与动态链接相关的导入…

java线程池怎么创建_java中的线程池,如何创建?

Java中的线程池它是线程的容器,或者(换句话说,它是具有执行任务能力的线程的集合)。我们可以使用ThreadPool框架来定位(或实现)线程池。线程池可以包含多个线程。每当我们执行任何任务时,线程就会从线程池中出来并完成该任务,然后…

JAVA 基础3-数组

一.数组的概念 数组可以看成是多个数据类型的集合,是对这些数据进行统一的管理; 数组的变量是引用类型,数组本身是对象,数组中的每个元素相当于该对象的成员变量; 数组的元素可以是任何数据类型,包括基础数…

net Core做一个webApi的简单实例

用NetCore 和Dapper 和mySql做一个简单的实例, 一准备工作 1:VS2017windos系统,也可以用其他的操作系统和工具 2:一台Cenetos的虚拟机或者虚拟机 二:开始 1:用微软官方的netCore的ToDo项目改造,…

mysql binlog sql统计_mysql的binlog详解

binlog是mysql记录操作的二进制日志文件,有三种格式可选,但是老旧的SBR已经不适合现在大多数业务需求,所以大多数都建议用MBR和RBR,即mixed或row,而解析他的原因,几乎都只有一个,就是恢复数据库,或者是反编译来恢复数据库,目的都是一样.以下是基于mysql5.6的mysqlbinlog来描述的…

Android项目实战(二十二):启动另一个APP or 重启本APP

Android项目实战(二十二):启动另一个APP or 重启本APP 原文:Android项目实战(二十二):启动另一个APP or 重启本APP一、启动另一个APP 目前公司项目需求,一个主APP,需要打开某些小APP&#xff0c…

mysql 查询两张表结构相同的数据库_利用反射处理多个表结构相同的数据的查询和数据库表的关联...

最近做一个项目,需要对人口数据进行查询,但是人口数据分布在不同的街道表中,首先进行了数据表结构的统一,每个数据表以街道名开头,然后其他的名字都一样前期将各个表中的字段也进行了统一抽象出一张字典表将街道编号和…

oracle批量update

我个人觉得写的很好 http://blog.csdn.net/wanglilin/article/details/7200201 需求: 将t2(t_statbuf)表中id和t1(T_Mt)表相同的记录更新进t1表。 1.错误的写法: 1 update table_name t1 set (a,b,c)( select a,b,c from table_name_2 t2 where t1.at2.a…

java 文件输出流_Java 文件输出流

Java IO教程 - Java文件输出流创建输出流要写入文件,我们需要创建一个FileOutputStream类的对象,它将表示输出流。// Create a file output streamString destFile "test.txt";FileOutputStream fos new FileOutputStream(destFile);当写入文…

Docker自动补全容器名

Zsh Place the completion script in your /path/to/zsh/completion (typically ~/.zsh/completion/): 下载自动完成文件mkdir -p ~/.zsh/completion curl -L https://raw.githubusercontent.com/docker/compose/1.21.0/contrib/completion/zsh/_docker-compose > ~/.zsh/co…

java 打印 模板_Java输入输出模板

常规输入输出绝壁有毒~import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigDecimal;import java.math.BigInteger;import java.util.StringTokenizer;public class Main {public static void main(St…

MySQL5.7参数log_timestamps

最近测试MySQL 5.7.21 Community Server这个版本的MySQL数据库时,发现其错误日志的时间跟系统当前时间不一致,后面检查发现日期时间格式都是UTC时间,查了一下相关资料,原来在MySQL 5.7.2 之后日志文件里面的时间戳从默认的本地系…

java非堆内存_java – 监视JVM的非堆内存使用情况

我们通常处理OutOfMemoryError问题,因为堆或permgen大小配置问题。但是所有的JVM内存不是permgen或者heap。据我所知,它也可以与Threads / Stacks,本地JVM代码相关…但是使用pmap我可以看到进程分配了9.3G这是3.3G堆外内存使用。我不知道什么…

.net core HttpContext(Http上下文)

在.NET Core中,只有Controller才能直接使用 HttpContext ,其他地方需要通过HttpContextAccessor来访问 转载于:https://www.cnblogs.com/87Super/p/8975311.html

Tidb集群加mysql_TiDB - 快速入门,集群搭建

TiDB 是开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP)的融合型分布式数据库产品,具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生的分布式数据库、兼容 MySQL 5.7 …

一个ssm综合小案例-商品订单管理-第二天

准确来说是第二三天,一时兴起,把这个小项目一鼓作气写完了(较大的bug 均已被我手动捉出并 fix )才来写一篇博客。 接上文 第一天配置继续讲解: 转载于:https://www.cnblogs.com/Frank99/p/8975378.html

java运行时读取注解_Java自定义注解和运行时靠反射获取注解

转:1、Annotation的工作原理:JDK5.0中提供了注解的功能,允许开发者定义和使用自己的注解类型。该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰的class文件和一个注解处理工具…

python递归函数

递归函数简单来说就是函数的自我调用。使用递归函数很多时候可以使得代码简洁,优雅。可以把复杂的问题分解成简单的子问题。递归有无与伦比的魅力,从著名的计算机名言就可以看出递归的奇妙: To iterate is human,to recurse divine. 迭代者为…

java的jsp要下载吗_jsp、java下载附件

1 传入此jsp中的参数均已URLDencoder过。23 4 5 6 7 boolean isError false;8 String errorMsg "";9 response.reset();//可以加也可以不加10 request.setCharacterEncoding("UTF-8");11 String folder "news";12 if(request.getParameter(&q…

浅谈高斯消元的实现和简单应用

一、高斯消元的原理 对于n元的m个线性方程组成的方程组,我们将其以矩阵的形式记录下来: a11 a12 a13 ...... a1n b1 a21 a22 a23 ...... a2n b2 ... ... ... an1 an2 an3 ...... ann bn 然后进行初等行列变换,尝试构造出一个上三角矩阵&#…