内存屏障

原文地址  作者:  译者:一粟   校对:无叶,方腾飞

本文我将和大家讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术。

CPU使用了很多优化技术来实现一个目标:CPU执行单元的速度要远超主存访问速度。在上一篇文章 “Write Combing (合并写)”中我已经介绍了其中的一项技术。CPU避免内存访问延迟最常见的技术是将指令管道化,然后尽量重排这些管道的执行以最大化利用缓存,从而把因为缓存未命中引起的延迟降到最小。

当一个程序执行时,只要最终的结果是一样的,指令是否被重排并不重要。例如,在一个循环里,如果循环体内没用到这个计数器,循环的计数器什么时候更新(在循环开始,中间还是最后)并不重要。编译器和CPU可以自由的重排指令以最佳的利用CPU,只要下一次循环前更新该计数器即可。并且在循环执行中,这个变量可能一直存在寄存器上,并没有被推到缓存或主存,这样这个变量对其他CPU来说一直都是不可见的。 Read more

从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个核直接把数据内容发过去, 数据只需要传一次。

Read more

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的简单示意图.
Read more

阻塞队列

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

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


Read more

信号量

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

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

Java同步块

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

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

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

Read more

为什么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方法)三个方法来分析它们的弱一致问题。

Read more

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

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

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();
		}
	}
}

而不是像这样直接使用类中的字段:

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();
		}
	}
}

那么为什么要这么做,有什么理由或说法?

Read more

Java并发编程【1.2时代】

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

Read more

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

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

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

Read more

内存访问模型的重要性

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

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. 敏捷回顾
    Read more

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

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

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

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

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

return top