Java多线程学习笔记之二缓存
1、高速缓存
由来:处理器处理能力原因大于主内存(DRAM)访问速率,为了弥补这个差距,引入了高速缓存。
高速缓存是一种存取速率远比主内存大而容量远比主内存小的存储部件,每一个处理器都有其高速缓存。在引入高速缓存之后,处理器执行读、写操作时就不直接操作主内存,而是通过高速缓存执行的。变量名相当于内存地址,变量值相当于相应内存空间中存储的数据。可以理解为,高速缓存为程序中的数据做了一份对应主内存的副本,但高速缓存的存储容量是非常小的,这些数据并不会一直被高速缓存存储。高速缓存在内部结构相当于一个拉链散列表,包含若干个桶,每个桶又包含若干个缓存条目。
缓存条目分为三个部分:Tag、Data Block和Flag。Data Block也叫做缓存行,是高速缓存和主内存之间数据交换的最小单元,用于存储从内存中读取的或者准备写入内存的数据。Tag包含了与缓存行中数据相对应的内存地址的部分信息。Flag用于表示相应缓存行的状态。一个缓存行可以存若干个变量的值。
处理器在执行访问操作时会将相应的内存地址解码为tag、index和offest三部分。index相当于桶编号,用来定位内存地址对应的桶;tag通过与缓存条目的Tag进行比较,定位一个具体的缓存条目;一个缓存条目中存储多个变量,offest是缓存行内的位置偏移,可以定位一个变量在缓存行中存储的起始位置。根据内存地址的解码结果,在高速缓存中找到相应的数据,并且所在缓存条目的Flag表示是有效的,那么这个内存操作就称为缓存命中,否则,则称为缓存未命中。缓存未命中是,处理器会从主内存中加载并存入相应的缓存行中。
处理器一般都具有多个层次的高速缓存,一级缓存、二级缓存、三级缓存等。越靠近处理器的缓存,存取速率越快,容量越小。Linux系统可有通过lscpu查看高速缓存层次。如下图
2、缓存一致性协议
当多处理器操作共享变量时就会存在读脏数据和丢失更新的问题,为了解决这个问题,引入里缓存一致性协议。
MESI(Modified-Exclusive-Shared-Invalid)协议就是一种缓存一致性协议,它实现缓存一致性的思想类似于读写锁,对于同一地址的读操作是并发的,对于同一地址的写操作是独占的。通过一组状态和消息来实现这种控制。
MESI状态:
- Modified(更改过的,M):该状态表示相应缓存行对包含相应内存地址所做的更新结果数据。因为MESI协议中对写操作的地址是独占的,所以同一时刻,多个处理器上的高速缓存中Tag值相同的缓存条目中,只可能有一个缓存条目处于该状态,且该状态缓存条目的数据一定与主内存中数据不一致。
- Exclusive(独占的,E):该状态表示相应缓存行包含相应内存地址所对应的副本数据。该状态的缓存行独占了相应内存地址的副本数据,其余处理器中的高速缓存都将不再保留该数据的有效副本,且此时缓存行中的数据和主内存中包含的数据应该是一致的。
- Shared(共享的,S):该状态表示相应缓存行包含相应内存地址所对应的副本数据。如果一个缓存条目处于该状态,且其他处理器上也存在Tag值与该缓存条目Tag值相同的缓存条目,那么这些缓存条目也一定处于Shared状态。该状态下缓存行中包含的数据与主内存中的数据一致。
- Invalid(无效的,I):该状态表示相应缓存行中不包含任何内存地址对应的有效副本数据。该状态是缓存条目的初始状态。
MESI消息如下表:
消息名 | 消息类型 | 描述 |
Read | 请求 | 通知其他处理器、主内存当前主内存准备读取某个数据。该消息包含待读取数据的内存地址。 |
Read Response | 相应 | 该消息包含被请求读取的数据。该消息可能是主内存提供的,也可能是其他高速缓存提供的。 |
Invalidate | 请求 | 通知其他处理器将其高速缓存中指定内存地址对应的缓存条目的状态设置为I,即删除相应内存地址的副本数据(更改其缓存条目的Flag值) |
Invalidate Acknowledge | 相应 | 接收到Invalidate消息的处理器必须回复该消息,以表示删除了其高速缓存上相应的副本数据。 |
Read Invalidate | 请求 | 该消息是又Read和Invalidate消息组合而成的,用于通知其他处理器当前处理器准备更新一个数据,并请求其他处理器删除其高速缓存中相应的副本数据。接收到该消息的处理器必须回复Read Response和Invalidate Acknowledge消息。 |
Writeback | 请求 |
该消息包含要写入主内存的数据及其相应的内存地址。 |
假设多处理器要对A地址的B数据进行操作,B数据为共享变量。
读操作(简略):
写操作(简略):
3、硬件缓冲区:写缓冲器与无效化队列
MESI协议在解决缓存一致性问题的同时也引入了等待回复MESI消息的延迟问题,为了减少这种延迟,引入了写缓冲器和无效化队列。
写缓冲器:是处理器内部一个容量比高速缓存还要小的高速存储部件,每个处理器都有其写缓冲器,其内部可包含若干条目。一个处理器无法读取另一个处理器的写缓冲器上的内容。
处理器进行写操作时,若缓存条目状态为S,则将写操作相关数据写入写缓冲器中,并发送Invalidate消息。若缓存条目状态为I,则为写未命中,将写操作相关数据写入写缓冲器中,并发送Read Invalidate消息。这样,处理器在将操作相关数据写入写缓冲器就可以认为写操作已完成,即不需等待消息回复就可执行其他指令。而当处理器接收到其他处理器回复的针对同一套缓存条目的所有Invalidate Acknowledge时,会将写缓冲器中相应地址的写操作的结果写入相应的缓存行中,此时写操作相对于本处理器之外的其他处理器而言才算是完成。
无效化队列:处理器在接收到Invalidate消息后,并不删除消息中指定地址的副本数据,而是将消息存入无效化队列后就回复Invalidate Acknowledge消息,减少了写操作处理器的等待时间。
写缓冲器和无效化队列又会带来内存重排序和可见性问题。
Java多线程学习笔记之二缓存的更多相关文章
- Java多线程学习笔记(二)——Executor,Executors,ExecutorService比较
Executor:是Java线程池的超级接口:提供一个execute(Runnable command)方法;我们一般用它的继承接口ExecutorService. Executors:是java.u ...
- java 多线程学习笔记(二) -- IO密集型任务
IO密集型是指对IO操作较多的任务.下面以查询一些股票价格任务为例: YahooFinance.java public class YahooFinance { public static doubl ...
- Java多线程学习笔记(二)
三 多线程执行的共享数据和非共享数据: 共享数据:就是每个线程执行的时候共享数据使用,比如这个线程一个为5的数据,减少为4之后,另一个线程执行拿到的数据是4,两个线程执行的数据是共享的. 非共享数据: ...
- Java多线程学习笔记(一)——多线程实现和安全问题
1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...
- java多线程学习笔记——详细
一.线程类 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...
- JAVA多线程学习笔记(1)
JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...
- Java NIO 学习笔记(二)----聚集和分散,通道到通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java多线程学习笔记
进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...
- Java多线程学习笔记--生产消费者模式
实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...
随机推荐
- Silverlight 在ie8 下 报2152 错误
前几天改别人的一个silverlight程序,在项目属性上 选中了 “通过使用应用程序库缓存减小XAP 大小”,编译无错,发布无错误. 放服务器上测试: 站点绑定域名,使用ie9.ie10 都没有问题 ...
- 用ASP.NET实现下载远程图片保存到本地的方法 保存抓取远程图片的方法
以下介绍两种方法:1.利用WebRequest,WebResponse 类WebRequest wreq=WebRequest.Create("http://files.jb51.net/f ...
- EF只更新变化的字段
摘要 在使用EF的时候,由于表字段较多,所以在更新的时候,想要只更新变化的字段,有没有办法呢? 解决办法 代码片段 public async Task<int> UpdateAsync(T ...
- @Html.Raw() 与Newtonsoft.Json.JsonConvert.SerializeObject()
一.后台 ViewBag.TypeList = typeList; 二.前台C# @{ var typeListFirst = ViewBag.TypeList;} 三.前台js中 var t ...
- 【13】享元模式(FlyWeight Pattern)
一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,若重复地使用new创建这个对象的话,就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非常严重.享元模式可以 ...
- Windows系统的消息机制
1)Add the window to the clipboard viewer chain. 通过SetClipboardViewer()传入窗口句柄,所有监视剪贴板的窗口句柄会组成一个链表(后来者 ...
- linux系统编程:自己动手写一个ls命令
ls用于列举目录内容,要实现这个功能,毫无疑问,需要读取目录,涉及到两个api: opendir:DIR *opendir(const char *name), 传文件名,返回一个指针,指向目录序列 ...
- 根据python上下文管理,写一个在读文件内容前后自动打开关闭文件的程序
利用上下文管理实现读f文件前后自动打开关闭文件#在本目录创建f文件,内容写monkey代码如下 import contextlib #导入模块1 @contextlib.contextmanager# ...
- SSM 后台封装的有值, 到前台打印的时候没有值
原因: 实体类中的getting setting 方法没有配置,导致封装json 数据的时候没有封装进去
- idea加载JSTL库
被这个错误缠了很长时间,偶然解决.eclipse for EE里导入JSTL没有问题,在JetIdea里就报classnotfound的异常. 解决方案:打包方式fix一下 新建library fil ...