从Java视角理解系统结构(三)伪共享

从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态

从我的前一篇博文中, 我们知道了CPU缓存及缓存行的概念, 同时用一个例子说明了编写单线程Java代码时应该注意的问题. 下面我们讨论更为复杂, 而且更符合现实情况的多核编程时将会碰到的问题. 这些问题更容易犯, 连j.u.c包作者Doug Lea大师的JDK代码里也存在这些问题.

MESI协议及RFO请求
前一篇我们知道, 典型的CPU微架构有3级缓存, 每个核都有自己私有的L1, L2缓存. 那么多线程编程时, 另外一个核的线程想要访问当前核内L1, L2 缓存行的数据, 该怎么办呢?
有人说可以通过第2个核直接访问第1个核的缓存行. 这是可行的, 但这种方法不够快. 跨核访问需要通过Memory Controller(见上一篇的示意图), 典型的情况是第2个核经常访问第1个核的这条数据, 那么每次都有跨核的消耗. 更糟的情况是, 有可能第2个核与第1个核不在一个插槽内.况且Memory Controller的总线带宽是有限的, 扛不住这么多数据传输. 所以, CPU设计者们更偏向于另一种办法: 如果第2个核需要这份数据, 由第1个核直接把数据内容发过去, 数据只需要传一次。

阅读全文

Java视角理解系统结构

  1. 从Java视角理解系统结构(一)上下文切换
  2. 从Java视角理解系统结构(二)CPU缓存
  3. 从Java视角理解系统结构(三)伪共享

从Java视角理解系统结构(二)CPU缓存

从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态

众所周知, CPU是计算机的大脑, 它负责执行程序的指令; 内存负责存数据, 包括程序自身数据. 同样大家都知道, 内存比CPU慢很多. 其实在30年前, CPU的频率和内存总线的频率在同一个级别, 访问内存只比访问CPU寄存器慢一点儿. 由于内存的发展都到技术及成本的限制, 现在获取内存中的一条数据大概需要200多个CPU周期(CPU cycles), 而CPU寄存器一般情况下1个CPU周期就够了.

CPU缓存
网页浏览器为了加快速度,会在本机存缓存以前浏览过的数据; 传统数据库或NoSQL数据库为了加速查询, 常在内存设置一个缓存, 减少对磁盘(慢)的IO. 同样内存与CPU的速度相差太远, 于是CPU设计者们就给CPU加上了缓存(CPU Cache). 如果你需要对同一批数据操作很多次, 那么把数据放至离CPU更近的缓存, 会给程序带来很大的速度提升. 例如, 做一个循环计数, 把计数变量放到缓存里,就不用每次循环都往内存存取数据了. 下面是CPU Cache的简单示意图.
阅读全文

阻塞队列

原文地址  By Jakob Jenkov   翻译:寒桐 校对:方腾飞

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列,下图展示了如何通过阻塞队列来合作:


阅读全文

信号量

原文地址  By Jakob Jenkov  翻译:寒桐  校对:方腾飞

Semaphore(信号量) 是一个线程同步结构,用于在线程间传递信号,以避免出现信号丢失(译者注:下文会具体介绍),或者像锁一样用于保护一个关键区域。自从5.0开始,jdk在java.util.concurrent包里提供了Semaphore 的官方实现,因此大家不需要自己去实现Semaphore。但是还是很有必要去熟悉如何使用Semaphore及其背后的原理 阅读全文

Java同步块

原文链接 作者:Jakob Jenkov 译者:李同杰

Java 同步块(synchronized block)用来标记方法或者代码块是同步的。Java同步块用来避免竞争。本文介绍以下内容:

  • Java同步关键字(synchronzied)
  • 实例方法同步
  • 静态方法同步
  • 实例方法中同步块
  • 静态方法中同步块
  • Java同步示例

阅读全文

为什么ConcurrentHashMap是弱一致的

本文将用到Java内存模型的happens-before偏序关系(下文将简称为hb)以及ConcurrentHashMap的底层模型相关的知识。happens-before相关内容参见:JLS §17.4.5. Happens-before Order深入理解Java内存模型以及Happens before;ConcurrentHashMap的详细介绍以及底层原理见深入分析ConcurrentHashMap。本文将从ConcurrentHashMap的get,clear,iterator(entrySet、keySet、values方法)三个方法来分析它们的弱一致问题。

阅读全文

类中字段赋值给局部变量后再使用意义何在?

Concurrency-interest邮件列表中有人问了这么一个问题:ArrayBlockingQueue中有个对象字段lock,在ArrayBlockingQueue的很多方法中,使用这个lock时都将其先赋值给一个局部变量,然后再通过局部变量调用lock上的方法,而没有直接使用lock字段,如remainingCapacity方法中先将this.lock赋值给一个局部变量lock,然后再使用这个局部变量:
[code lang=”java”]
public class ArrayBlockingQueue {
private final ReentrantLock lock;

//…other fields and methods

public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.length – count;
} finally {
lock.unlock();
}
}
}
[/code]
而不是像这样直接使用类中的字段:
[code lang=”java”]
public class ArrayBlockingQueue {
private final ReentrantLock lock;

//…other fields and methods

public int remainingCapacity() {
this.lock.lock();
try {
return items.length – count;
} finally {
this.lock.unlock();
}
}
}
[/code]
那么为什么要这么做,有什么理由或说法?

阅读全文

Java并发编程【1.2时代】

         本文介绍了Java原生的多线程技术(1.2),通过详细介绍waitnotify相关的机制、基础的多线程技术以及基于这些技术的等待超时、线程间的通信技术和线程池高阶技术,最后通过一个基于线程池的简单文本web服务器—MollyServer,来阐明多线程带来好处。通过介绍这些技术,展示了在没有使用Java并发包的时代(1.5-)是如何完成Java的多线程编程,为理解Java5提供了良好帮助。

阅读全文

有助于减少伪共享的@Contended注解

原文链接 作者:Dave 译者:卓二妹 校对:丁一

详细描述看Aleksey Shipilev这封邮件 —— 我们期待@Contended已久。JVM会自动为对象字段进行内存布局。通常JVM会这样做:(a)将对象的域按从大到小的顺序排列,以优化占用的空间;(b)打包引用类型的字段,以便垃圾收集器在追踪的时候能够处理相连的引用类型的字段。@Contended让程序能够更明确地控制并发和伪共享。通过该功能我们能够把那些频繁进行写操作的共享字段,从其它几乎是只读或只有少许写操作的字段中分离开来。原则很简单:对共享内容进行读操作的代价小,对共享内容的写操作代价非常高。我们也可以将那些可能会被同一个线程几乎同时写的字段打包到一起。

阅读全文

内存访问模型的重要性

在高性能的计算中,我们常说缓存失效(cache-miss)是一个算法中最大性能损失点。 近些年来,我们的处理器处理能力的增长速度已经大大超过了访问主内存的延迟的缩短。 通过更宽的,多通道的总线,到主内存的带宽已经大大增加,但延迟并没有相应显著减少。 为了减少延迟,处理器采用愈加复杂的多层的高速缓存子系统。
在1994年的一篇论文“Hitting the memory wall: implications of the obvious”中描述了这个问题,并且认为由于缓存失效(cache-miss)必定存在,缓存不能最终解决这个问题。我的目标是:向大家展示通过利用访问模型,它是对缓存层次结构的思考,上面的结论并不是必然的。
让我们带着问题,来看下面的例子, 我们的硬件试图通过一些技术来减少主内存的延迟。 内存访问模型主要基于下面三个规律:
1、时间局部性 :最近访问过的内存最可能立马再次被访问;
2、空间局部性:相邻的内存最可能立马再次被访问;
3、 跨越式:内存访问可能会遵循一种可预测的模式。
阅读全文

Mechanical Sympathy 译文

Mechanical Sympathy(需要翻墙才能访问)是Martin Thompson的博客,这个博客主要讲的是底层硬件是如何运作的,以及如何编程能够与地层硬件良好的协作。英文名称的为未翻译的文章,有兴趣的同学可以领取翻译。

  1. CPU缓存刷新的误解
  2. 伪共享
  3. Java 7与伪共享的新仇旧恨
  4. 内存访问模型的重要性
  5. Memory Barriers/Fences
  6. 合并写
  7. 内存屏障

阿里内贸团队敏捷实践

本实践是原阿里巴巴ITU内贸团队打造出的一系列适合团队不断精进的敏捷实践。阿里内贸团队是一支自组织,一体化和全功能的敏捷团队。自组织体现在团队中的每个成员都会得到充分的尊重和自由,每个成员都可以主导某个实践(如项目回顾会议)和改进(如推动团队成员一起提高单元测试覆盖率)。一体化体现在团队合作上。全功能体现在团队成员既可以研发,也可以做前端和测试,从而打破角色边界提高总体产出。我们在进行一次敏捷实践时会经历五个步骤,分别是计划编码测试发布回顾。注:本图由同事金建法所画。

  1. 打造合作型团队
  2. 自组织管理
  3. 敏捷设计
  4. 结对编程
  5. 迭代计划
  6. 开发自测
  7. Code Review
  8. 敏捷回顾
    阅读全文

阿里内贸团队敏捷实践(二)自组织管理

本文是作者原创,原文发表于《程序员》杂志2013年3月刊

实现团队的自组织管理,非常有助于团队形成合力,极大地提升团队整体的工作效率。本文结合原阿里ITU内贸团队的敏捷实践经历,阐释了何为自组织管理、为什么进行自组织管理、如何进行自组织管理等内容,同时给出了团队实施自组织管理的效果。

在《射雕英雄传》里,以全真七子的武功是打不过东邪黄药师的,但当他们摆出了“天罡北斗阵”时,却能和黄药师打成平手。这就是团队合作形成合力的威力。

自组织管理是原阿里ITU内贸团队采取的一种敏捷实践,该实践旨在帮助团队成员加强团队合作,形成团队的合力,从而提高团队整体的工作效率。 阅读全文

阿里内贸团队敏捷实践(三)结对编程

原文发表于《程序员》杂志2012年2月刊

本文主要从提升项目质量、促进知识传递及减少项目风险等角度出发,讲述作者所在团队在结对编程实践中的一些经历,以及如何避免或减少其所带来的负面影响。

你了解结对编程吗?你尝试过结对编程实践吗?也许你还未曾尝试甚至还不曾了解,那么我们一起来学习和了解敏捷结对编程实践,相信对敏捷感兴趣的你会有收获。 阅读全文

return top