Java内存模型Cookbook(二)内存屏障

原文:http://gee.cs.oswego.edu/dl/jmm/cookbook.html 第二节

作者:Doug Lea 翻译:潘曦   校对:方腾飞

  1. 指令重排
  2. 内存屏障
  3. 多处理器
  4. 指南

编译器和处理器必须同时遵守重排规则。由于单核处理器能确保与“顺序执行”相同的一致性,所以在单核处理器上并不需要专门做什么处理,就可以保证正确的执行顺序。但在多核处理器上通常需要使用内存屏障指令来确保这种一致性。即使编译器优化掉了一个字段访问(例如,因为一个读入的值未被使用),这种情况下还是需要产生内存屏障,就好像这个访问仍然需要保护。(可以参考下面的优化掉内存屏障的章节)。

内存屏障仅仅与内存模型中“获取”、“释放”这些高层次概念有间接的关系。内存屏障并不是“同步屏障”,内存屏障也与在一些垃圾回收机制中“写屏障(write barriers)”的概念无关。内存屏障指令仅仅直接控制CPU与其缓存之间,CPU与其准备将数据写入主存或者写入等待读取、预测指令执行的缓冲中的写缓冲之间的相互操作。这些操作可能导致缓冲、主内存和其他处理器做进一步的交互。但在JAVA内存模型规范中,没有强制处理器之间的交互方式,只要数据最终变为全局可用,就是说在所有处理器中可见,并当这些数据可见时可以获取它们。
阅读全文

LMAX架构

原文地址:http://martinfowler.com/articles/lmax.html 作者:Martin Fowler

译文地址:http://www.jdon.com/42452 译者:banq

LMAX是一种新型零售金融交易平台,它能够以很低的延迟(latency)产生大量交易(吞吐量). 这个系统是建立在JVM平台上,核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单. 业务逻辑处理器完全是运行在内存中(in-memory),使用事件源驱动方式(event sourcing). 业务逻辑处理器的核心是Disruptors,这是一个并发组件,能够在无锁的情况下实现网络的Queue并发操作。他们的研究表明,现在的所谓高性能研究方向似乎和现代CPU设计是相左的。

目录

  1. 整体架构
  2. 业务逻辑处理
  3. Disruptors的输入和输出
  4. 队列和机制偏爱的缺乏
  5. 你需要使用这个架构吗

 

过去几年我们不断提供这样声音:免费午餐已经结束。我们不再能期望在单个CPU上获得更快的性能,因此我们需要写使用多核处理的并发软件,不幸的是, 编写并发软件是很难的,锁和信号量是很难理解的和难以测试,这意味着我们要花更多时间在计算机上,而不是我们的领域问题,各种并发模型,如Actors 和软事务STM(Software Transactional Memory), 目的是更加容易使用,但是按下葫芦飘起瓢,还是带来了bugs和复杂性.

我很惊讶听到去年3月QCon上一个演讲, LMAX是一种新的零售的金融交易平台。它的业务创新 – 允许任何人在一系列的金融衍生产品交易。这就需要非常低的延迟,非常快速的处理,因为市场变化很快,这个零售平台因为有很多人同时操作自然具备了复杂性,用户越多,交易量越大,不断快速增长。

鉴于多核心思想的转变,这种苛刻的性能自然会提出一个明确的并行编程模型 ,但是他们却提出用一个线程处理6百万订单,而且是每秒,在通用的硬件上。

通过低延迟处理大量交易,取得低延迟和高吞吐量,而且没有并发代码的复杂性,他们是怎么做到呢?现在LMAX已经产品化一段时间了,现在应该可以揭开其神秘而迷人的面纱了。

阅读全文

Java内存模型Cookbook(三)多处理器

原文:http://gee.cs.oswego.edu/dl/jmm/cookbook.html

作者:Doug Lea 翻译:古圣昌   校对:欧振聪,方腾飞

  1. 指令重排
  2. 内存屏障
  3. 多处理器
  4. 指南

本文总结了在多处理器(MPs)中常用的的处理器列表,处理器相关的信息都可以从链接指向的文档中得到(一些网站需要通过注册才能得到相应的手册)。当然,这不是一个完全详细的列表,但已经包括了我所知道的在当前或者将来Java实现中所使用的多核处理器。下面所述的关于处理器的列表和内容也不一定权威。我只是总结一下我所阅读过的文档,但是这些文档也有可能是被我误解了,一些参考手册也没有把Java内存模型(JMM)相关的内容阐述清楚,所以请协助我把本文变得更准确。
阅读全文

同步和Java内存模型 (三)可见性

原文:http://gee.cs.oswego.edu/dl/cpj/jmm.html 第三章
作者:Doug Lea 译者:程晓明 校对:方腾飞

只有在下列情况时,一个线程对字段的修改才能确保对另一个线程可见:

一个写线程释放一个锁之后,另一个读线程随后获取了同一个锁。本质上,线程释放锁时会将强制刷新工作内存中的脏数据到主内存中,获取一个锁将强制线程装载(或重新装载)字段的值。锁提供对一个同步方法或块的互斥性执行,线程执行获取锁和释放锁时,所有对字段的访问的内存效果都是已定义的。

注意同步的双重含义:锁提供高级同步协议,同时在线程执行同步方法或块时,内存系统(有时通过内存屏障指令)保证值的一致性。这说明,与顺序程序设计相比较,并发程序设计与分布式程序设计更加类似。同步的第二个特性可以视为一种机制:一个线程在运行已同步方法时,它将发送和/或接收其他线程在同步方法中对变量所做的修改。从这一点来说,使用锁和发送消息仅仅是语法不同而已。

阅读全文

Dissecting the Disruptor: Demystifying Memory Barriers

原文地址:http://mechanitis.blogspot.com/2011/08/dissecting-disruptor-why-its-so-fast.html (因有墙,移到墙内)

My recent slow-down in posting is because I’ve been trying to write a post explaining memory barriersand their applicability in the Disruptor. The problem is, no matter how much I read and no matter how many times I ask the ever-patient Martin and Mike questions trying to clarify some point, I just don’t intuitively grasp the subject. I guess I don’t have the deep background knowledge required to fully understand.

So, rather than make an idiot of myself trying to explain something I don’t really get, I’m going to try and cover, at an abstract / massive-simplification level, what I do understand in the area.  Martin has written a post going into memory barriers in some detail, so hopefully I can get away with skimming the subject.

阅读全文

Dissecting the Disruptor: Why it’s so fast (part two) – Magic cache line padding

原文地址:http://mechanitis.blogspot.com/2011/07/dissecting-disruptor-why-its-so-fast_22.html(因有墙移到墙内)

We mention the phrase Mechanical Sympathy quite a lot, in fact it’s even Martin’s blog title.  It’s about understanding how the underlying hardware operates and programming in a way that works with that, not against it.

We get a number of comments and questions about the mysterious cache line padding in theRingBuffer, and I referred to it in the last post.  Since this lends itself to pretty pictures, it’s the next thing I thought I would tackle.
阅读全文

Dissecting the Disruptor: Why it’s so fast (part one) – Locks Are Bad

原文地址:http://mechanitis.blogspot.com/2011/07/dissecting-disruptor-why-its-so-fast.html(因前方有墙,所以我移到墙内)

Martin Fowler has written a really good article describing not only the Disruptor, but also how it fits into the architecture at LMAX.  This gives some of the context that has been missing so far, but the most frequently asked question is still “What is the Disruptor?”.

I’m working up to answering that.  I’m currently on question number two: “Why is it so fast?”.

 

These questions do go hand in hand, however, because I can’t talk about why it’s fast without saying what it does, and I can’t talk about what it is without saying why it is that way.

So I’m trapped in a circular dependency.  A circular dependency of blogging.

To break the dependency, I’m going to answer question one with the simplest answer, and with any luck I’ll come back to it in a later post if it still needs explanation: the Disruptor is a way to pass information between threads.
阅读全文

并发译文翻译计划(二)

Doug Lea 的文献

  1. Synchronizer Framework  译者:ClarenceAu (已翻译完成,在校对)
  2. Fork/Join  译者:Alex(陆续发表中)
  3. Java Concurrency Constructs 译者:萧欢(已翻译完成)
  4. Cancellation 译者:丁一  (已翻译完成)

以下文章来自:https://code.google.com/p/disruptor/wiki/BlogsAndArticles

如何使用Disruptor

  1. What’s so special about a ring buffer? – A summary by Trisha of the data structure at the heart of the Disruptor patter, how it’s implemented and what’s so great about it. (译者:淘宝-欧立勇)
  2. How do I read from a ring buffer? – Trisha gives an overview of the Consumer and ConsumerBarrier, which allows you to read stuff off the ring buffer.(译者:古圣昌)
  3. Writing to the ring buffer – The third piece from Trisha explaining how to write to the ring buffer, and how it avoids wrapping.(译者:长源)
  4. Lock-free publishing – Danny outlines the concepts behind putting items into the ring buffer.(译者:行知)
  5. DSL for wiring up the Disruptor – Adrian came up with a cunning way to configure your Disruptor(译者:杨帆)
  6. Disruptor wizard now part of the Disruptor – Adrian’s wizard now makes it easy to configure your very own Disruptor straight out of the box (译者:杨帆)
  7. Disruptor version 2.0 – Trisha outlines the cosmetic changes to the Disruptor in version 2.0.(译者:杨帆)
  8. Sharing Data Among Threads Without Contention – An updated and summarised version of Trisha’s blogs in Oracle’s Java Magazine.(译者:同杰)

Disruptor为什么这么快

  1. Locks Are Bad – Trisha gives some basic concurrency background and explains why locks are evil. (译者:nick,潘曦,已经翻译完成)
  2. Magic cache line padding – An explanation around why the odd cache line padding variables are required, by Trisha.(译者:方腾飞,已经翻译完)
  3. Demystifying Memory Barriers – Trisha attempts to explain why memory barriers are important in the Disruptor. (译者:杜建雄)

其他人写的Disruptor文章

  1. LMAX 架构  by Martin Fowler (已翻译)
  2. Processing 1m TPS with the Axon Framework using the Disruptor.(译者,程晓明)

有兴趣的同学可以一起参与,有什么其他并发文献希望我们翻译的也可以通过留言告知我们。

如何翻译

  1. 你可从以上几篇文章中挑选某一篇进行翻译,翻译时间最好是一个星期以内,翻译前请发邮件到main_shorttime(AT)163.com告诉我你要翻译的文章和预计完成时间。
  2. 译者署名的顺序由翻译的字数确定。
  3. 与其他译者交叉校对,互相讨论翻译与技术问题。
  4. 提交翻译:在并发编程网用QQ登陆,然后发布译文。

注意事项

  1. 本文档的传播是基于学习研究而非商业,因此翻译纯属兴趣和分享精神。
  2. 对译者的要求,因为我们是出于学习和研究目的,所以对译者没有很高的要求,只要你只要你对并发编程感兴趣,并且愿意用心来翻译文章,翻译完的文章首先自己能读明白就行,放心我们会进行校对。

聊聊并发(六)ConcurrentLinkedQueue的实现原理分析

本文是作者原创,首发于InfoQ:http://www.infoq.com/cn/articles/ConcurrentLinkedQueue

1.    引言

在并发编程中我们有时候需要使用线程安全的队列。如果我们要实现一个线程安全的队列有两种实现方式一种是使用阻塞算法,另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,而非阻塞的实现方式则可以使用循环CAS的方式来实现,本文让我们一起来研究下Doug Lea是如何使用非阻塞的方式来实现线程安全队列ConcurrentLinkedQueue的,相信从大师身上我们能学到不少并发编程的技巧。
阅读全文

看动画学并发编程

Java提供了并发库简化了并发编程,但这是很难用可视化的方式来学习。 Java Concurrent Animated项目用一系列的动画来演示每个java并发库组件和代码。看动画学并发编程

Java Concurrent Animated程序,为每一个并发组件的学习点,开发一个演示动画的程序,并配合代码和一张PPT进行知识点的说明。

官方网址是:http://sourceforge.net/projects/javaconcurrenta/

下载地址:http://sourceforge.net/projects/javaconcurrenta/files/latest/download?source=files

同步和Java内存模型(五)Volatile

原文链接: http://gee.cs.oswego.edu/dl/cpj/jmm.html

作者:Doug lea 译者:杜建雄  校对者:方腾飞

Volatile

从原子性,可见性和有序性的角度分析,声明为volatile字段的作用相当于一个类通过get/set同步方法保护普通字段,如下:

final class VFloat {
    private float value;

    final synchronized void set(float f) { value = f; }
    final synchronized float get()       { return value; }
}

与使用synchronized相比,声明一个volatile字段的区别在于没有涉及到锁操作。但特别的是对volatile字段进行“++”这样的读写操作不会被当做原子操作执行。

另外,有序性和可见性仅对volatile字段进行一次读取或更新操作起作用。声明一个引用变量为volatile,不能保证通过该引用变量访问到的非volatile变量的可见性。同理,声明一个数组变量为volatile不能确保数组内元素的可见性。volatile的特性不能在数组内传递,因为数组里的元素不能被声明为volatile。

阅读全文

Happens before

原文:http://www.cs.umd.edu/class/fall2010/cmsc433/lectures/happens-before.txt

译者:丁一

“Happens before”是由Leslie Lamport引入的用来描述程序事件的一种偏序关系。

将多线程的执行看作是事件E的轨迹R,定义如下(轨迹只是一种次序):

Events E ::= start(T)
	  |  end(T)
	  |  read(T,x,v)
  	|  write(T,x,v)
	  |  spawn(T1,T2)
	  |  join(T1,T2)
	  |  lock(T,x)
	  |  unlock(T,x)

这里T是一个线程标识符,x是一个变量,v是一个值。read(T,x,v)事件表示线程T从变量x中读出值v。同时假定轨迹R是结构良好的,即要求在R中,线程T的第一个事件必须是start(T)。在end(T)之后不再有线程T相关的事件。
阅读全文

同步与Java内存模型(一)序言

原文:http://gee.cs.oswego.edu/dl/cpj/jmm.html

作者:Doug Lea 译者:萧欢  校对:丁一,方腾飞

先来看如下这个简单的Java类,该类中并没有使用任何的同步。

final class SetCheck {
private int  a = 0;
private long b = 0;

void set() {
a =  1;
b = -1;
}

boolean check() {
return ((b ==  0) ||
(b == -1 && a == 1));
}
}

如果是在一个串行执行的语言中,执行SetCheck类中的check方法永远不会返回false,即使编译器,运行时和计算机硬件并没有按照你所期望的逻辑来处理这段程序,该方法依然不会返回false。在程序执行过程中,下面这些你所不能预料的行为都是可能发生的:

  • 编译器可能会进行指令重排序,所以b变量的赋值操作可能先于a变量。如果是一个内联方法,编译器可能更甚一步将该方法的指令与其他语句进行重排序。
  • 处理器可能会对语句所对应的机器指令进行重排序之后再执行,甚至并发地去执行。
  • 
内存系统(由高速缓存控制单元组成)可能会对变量所对应的内存单元的写操作指令进行重排序。重排之后的写操作可能会对其他的计算/内存操作造成覆盖。
  • 编译器,处理器以及内存系统可能会让两条语句的机器指令交错。比如在32位机器上,b变量的高位字节先被写入,然后是a变量,紧接着才会是b变量的低位字节。
  • 编译器,处理器以及内存系统可能会导致代表两个变量的内存单元在(如果有的话)连续的check调用(如果有的话)之后的某个时刻才更新,而以这种方式保存相应的值(如在CPU寄存器中)仍会得到预期的结果(check永远不会返回false)。

阅读全文

Java Fork Join框架 (三) 设计

原文 http://gee.cs.oswego.edu/dl/papers/fj.pdf

作者:Doug Lea
译者:Alex

Fork/Join程序可以在任何支持以下特性的框架之上运行:框架能够让构建的子任务并行执行,并且拥有一种等待子任务运行结束的机制。然而,java.lang.Thread类(同时也包括POSIX pthreads, 这些也是Java线程所基于的基础)对Fork/Join程序来说并不是最优的选择:

  • Fork/Join 任务对同步和管理有简单的和常规的需求。相对于常规的线程来说,fork/join任务所展示的计算布局将会带来更加灵活的调度策略。例如,fork/join任务除了等待子任务外,其他情况下是不需要阻塞的。因此传统的用于跟踪记录阻塞线程的代价在这种情况下实际上是一种浪费。
  • 对于一个合理的基础任务粒度来说,构建和管理一个线程的代价甚至可以比任务执行本身所花费的代价更大。尽管粒度是应该随着应用程序在不同特定平台上运行而做出相应调整的。但是超过线程开销的极端粗粒度会限制并行的发挥。

简而言之,Java标准的线程框架对fork/join程序而言太笨重了。但是既然线程构成了很多其他的并发和并行编程的基础,完全消除这种代价或者为了这种方式而调整线程调度是不可能(或者说不切实际的)。

阅读全文

ConcurrentHashMap能完全替代HashTable吗?

回答:hash table虽然性能上不如ConcurrentHashMap,但并不能完全被取代,两者的迭代器的一致性不同的,hash table的迭代器是强一致性的,而concurrenthashmap是弱一致的。 ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea 也将这个判断留给用户自己决定是否使用ConcurrentHashMap。

 

return top