1.什么是缓存对齐

当前的电脑中,数据存储在磁盘上,可以断电保存,但是读取效率较低。不断电的情况下,数据可以在内存中存储,相对硬盘效率差不多是磁盘的一万倍左右。但是运算时,速度最快的是直接缓存在CPU中的数据。CPU有三级缓存分别是L1,L2,L3三级,CPU访问速度大概是内存的100倍。

1.1CPU结构

对于一台电脑,其主板可以支持多少个CPU插槽,称为CPU个数。对于一颗多核CPU,单片CPU上集成的处理核心称为CPU核数。对于每个核心,可以给每个核设置两组寄存器,两组pc。

CPU结构如上图所示(图片来自网络),对于一块CPU,可以有多个处理核心。每个核心内有自己的L1,L2缓存,多个核心共用同一个L3缓存。但一个电脑如果有多个CPU插槽,各个CPU有自己的L3。对于一个CPU核心来说,每个核心都有ALU,逻辑运算单元。负责对指令进行计算。Register 寄存器,记录线程执行对应的数据。PC:指令寄存器,记录线程执行到了哪个位置。里面存的是指令行数。通俗讲,就是记录线程执行到了哪一行指令(代码在进入CPU运行前,会被编译成指令)了。

线程在执行的时候,将当前线程对应的数据放入寄存器,将执行行数放到指令寄存器,然后执行过一个时间片后,如果线程没有执行完,将数据和指令保存,然后其他线程进入执行。一个ALU对应多个PC|registers的时候(所谓的四核八线程)。一般来说,同一个CPU核在同一个时间点,只能执行同一个线程,但是,如果一个核里面有两组寄存器,两个pc。那么就可以同时执行两组线程,在切换线程的时候,没必要再去等待寄存器的数据保存和数据载入。直接切换到下一组寄存器就可以。这就是超线程。

1.2缓存对齐

CPU到内存之间有很多层的内存,如图所示,CPU需要经过L1,L2,L3及主内存才能读到数据。从主内存读取数据时的过程如下:

当我左侧的CPU读取x的值的时候,首先会去L1缓存中去找x的值,如果没有,那么取L2,L3依次去找。最后从主内存读入的时候,首先将内存数据读入L3,然后L2最后L1,然后再进行运算。但是读取的时候,并不是只读一个X的值,而是按块去读取(跟电脑的总线宽度有关,一次读取一块的数据,效率更高)。CPU读取X后,很可能会用到相邻的数据,所以在读X的时候,会把同一块中的Y数据也读进来。这样在用Y的时候,直接从L1中取数据就可以了。

读取的块就叫做缓存行,cache line 。缓存行越大,局部性空间效率越高,但读取时间慢。缓存行越小,局部性空间效率越低,但读取时间快。目前多取一个平衡的值,64字节。

然后,如果你的X和y在同一块缓存行中,且两个字段都用volatile修饰了,那么将来两个线程再修改的时候,就需要将x和y发生修改的消息高速另外一个线程,让它重新加载对应缓存,然而另外一个线程并没有使用该缓存行中对应的内容,只是因为缓存行读取的时候跟变量相邻,这就会产生效率问题。

解决起来也简单,我们将数据中的两个volatile之间插入一些无用的内存,将第二个值挤出当前缓存行,那么执行的时候,就不会出现相应问题了。提高代码效率。

2.缓存对齐在java中实现

在java中,jdk一些涉及到多线程的类,有时候会看到类似于public volatile long p1,p2,p3,p4,p5,p6,p7;这样的代码,有的就是做的缓存行对齐。

我们设计一个实验去验证缓存行对齐的导致的性能问题,及相关的解决后的效率问题。具体代码见第三小节。这里的思路是,首先,我们写一个类T,这个类里面有一个用volatile修饰的long属性的值,这个值占用8个字节。然后声明一个静态数组,包含两个元素,分别T的两个对象。然后开启两个线程,让两个线程分别给数组的第一个值和第二个值赋值,执行一百万次,看执行的耗时。

这个时候,代码执行的时候如1.2的图中所示,假设数组中第一个值为X,第二个值为Y。左侧框内为第一个线程,执行修改X值的操作,右侧框内为第二个线程,修改Y的值。因为两个值在同一个缓存行中,所以在X值在读取的时候,同时将X值和Y值一起读入缓存。第二个线程只修改Y的值,但是同样将XY全部读入缓存。线程1中X值发生修改后,第二个线程中的X值需要进行更新。而线程2修改Y的值后也需要同样的操作,但是这个更新不是必要的,而且会影响执行的效率。

解决方法是:我们给第T的long值之前加入8个long值,这样Y值就会被挤到其他缓存行,这样彼此修改的时候就不会产生干扰,提高代码执行效率。

下面是具体验证的代码,其中在没有加入父类的时候,是相互干扰时的执行耗时。第二个是加入父类后,不再干扰时的耗时,执行后可以看出,第二套代码在执行的时候,代码要优于第一套代码的执行。

3.缓存对齐的代码实现

1 public classT01_CacheLinePadding {2 private static classT{3 public long x = 0L;4 }5 public static T[] orr = new T[2];6 static{7 orr[0]= newT();8 orr[1]= newT();9 }10 public static void main(String[] args) throwsException {11 Thread t1 = new Thread(()->{12 for (long i = 0; i < 1000_000L; i++) {13 orr[0].x =i;14 }15 });16 Thread t2 = new Thread(()->{17 for (long i = 0; i < 1000_000L; i++) {18 orr[1].x =i;19 }20 });21 final long start =System.nanoTime();22 t1.start();23 t2.start();24 t1.join();25 t2.join();26 System.out.println((System.nanoTime()-start)/100_000);27 }28 }

1 packagemsb;2 /**

3 * 缓存行对齐问题代码4 *@authorL Ys5 *6 */

7 public classT02_CacheLinePadding {8 private static classPadding{9 public longp1,p2,p3,p4,p5,p6,p7;10 }11 private static class T extendsPadding{12 public volatile long x = 0L;13 }14 public static T[] orr = new T[2];15 static{16 orr[0]= newT();17 orr[1]= newT();18 }19 public static void main(String[] args) throwsException {20 Thread t1 = new Thread(()->{21 for (long i = 0; i < 1000_000L; i++) {22 orr[0].x =i;23 }24 });25 Thread t2 = new Thread(()->{26 for (long i = 0; i < 1000_000L; i++) {27 orr[1].x =i;28 }29 });30 final long start =System.nanoTime();31 t1.start();32 t2.start();33 t1.join();34 t2.join();35 System.out.println((System.nanoTime()-start)/100_000);36 }37 }

程序员灯塔

转载请注明原文链接:Java多线程_缓存对齐

java 多线程写缓存,Java多线程_缓存对齐相关推荐

  1. 怎么才能学好Java编程写好Java代码?

    动力节点Java培训最新上线Java实验班,等你来测试自己适不适合学习Java编程哦! 刚刚接触Java编程的朋友总会遇到一些情况,比如:跟着Java基础教程看过一遍后,自己写代码的时候却无从下手:写 ...

  2. java多线程写在哪一层_面试知识点三:Java多线程

    35.并行和并发有什么区别? 36.线程和进程的区别? 37.守护线程是什么? 38.创建线程有哪几种方式? 39.说一下 runnable 和 callable 有什么区别? 40.线程有哪些状态? ...

  3. java如何写线程外部类_廖雪峰Java读书笔记(六)--多线程(或称并发)

    1. 多线程基础 首先要明白一些概念: 进程:把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程,类似的,音乐播放器和Word都是进程. 线程:某些进程内部还需要同时执行多个子任务.例 ...

  4. java 最少使用(lru)置换算法_缓存置换算法 - LRU算法

    LRU算法 1 原理 对于在内存中并且不被使用的数据块就是LRU,这类数据需要从内存中删除,以腾出空间来存储常用的数据. LRU算法(Least Recently Used,最近最少使用),是内存管理 ...

  5. java 缓存行填充_缓存伪共享问题以及解决方案缓存行填充

    缓存伪共享 共享对象存在同一个缓存中,由于MESI协议,一个对象中一些不需要改变的属性因为其他改变的属性,导致整个对象的缓存进入到M被修改状态. 目前的CPU是通常按照32或者64字节的缓存行(Cac ...

  6. JAVA怎么写自动阅读视频_自己手写屏幕记录位置并扫描课程自动播放学习视频...

    用来学习视频赚积分用的,后来厂里出了1分钟不动鼠标就暂停的版本就没用了. 纯娱乐,记录下来. public static void main(String[] args) throws Excepti ...

  7. java如何写ajax,java,jq,ajax写分页

    1.先写好html基础样式 我懒得去写css样式233,能看就行 #page { width: 20px; } id name pwd age 上一页 下一页 2.编写servlet 2.1 先写查询 ...

  8. linux 多线程 写日志,rsyslog多线程远程日志记录介绍(lamp+rsyslog)

    rsyslog多线程远程日志记录介绍(lamp+rsyslog) rsyslog: rsyslog: 多线程: 支持UDP, TCP, SSL, TLS, RELP远程日志记录 rsyslog支持将日 ...

  9. java常规普氏分析法_人脸对齐:Procrustes analysis 普氏分析

    概述 在人脸相关应用中,获得的人脸图像常常形状各异,这时就需要对人脸形状进行归一化处理.人脸对齐就是将两个不同的形状进行归一化的过程,将一个形状尽可能地贴近另一个形状. 值得注意的是,在英语文献中,F ...

  10. python多线程写日志_python 多线程logger问题

    展开全部 因为logging是threadsafe的,但不是process-safe(应该没有这个词儿,只是为了便于理解)的.这段代码就是多个进程共同操e69da5e6ba90323131333532 ...

最新文章

  1. 串口监视软件_ESP32 Arduino教程:软件重置
  2. Python3中上下文管理器介绍
  3. 微服务架构实施原理详解
  4. linux系统宿主定制之初窥门径
  5. det--求矩阵的行列式
  6. crontab 半小时_?用猫粮诱惑,7名消防员花半小时救出困在夹墙间的喵星人
  7. 数据挖掘之KNN分类
  8. 如何使Putty会话颜色更美观
  9. 程序员福利各大平台免费接口,非常适用
  10. stm32for循环几个机械周期_带你了解包装机械设备的可调度性分析
  11. silverlight3.0 怎么调试XAML对应的XAML.CS文件
  12. android sdk环境配置_Mac 配置adb环境的方法
  13. 使用Python写一个m3u8多线程下载器
  14. php打开word文件怎么打开,docx文件怎样打开
  15. nnet3-compute计算chain前向传播概率矩阵(声学模型输出)
  16. 教程:这两个小妙招手把手教会你白底图片怎么抠图
  17. JAVA:实现QuickSort快速排序算法(附完整源码)
  18. UE使用技巧之---去空格
  19. javaweb人事与工资管理系统
  20. 最好的智慧养老解决方案在哪里?智慧养老解决方案在这里-新导智能

热门文章

  1. 【渝粤题库】陕西师范大学500013 物理教学论 作业(专升本)
  2. 【渝粤题库】陕西师范大学500004 电动力学
  3. 物联网技术在智慧消防中的应用
  4. 亿佰特物联网通信专家:蓝牙模块和 Zigbee协议模块的区别
  5. 5W-Lora电台的远距离传输优势
  6. emacs c语言,如何利用Emacs来调试C++程序
  7. linux 下对u盘分区吗,linux对中毒u盘分区和格式化
  8. linux7禁用ipv6,RHEL 7 及 CentOS 7 彻底禁用IPv6的方法
  9. vc mysql 图片_VC连接MySQL
  10. 图神经网络PGL助力国民级音乐App,创新迭代千亿级推荐系统(人工智能应用案例)