内存屏障

http://ifeve.com/memory-barriers-or-fences/

内存屏障提供了两个功能。

     1、首先,它们通过确保从另一个CPU来看屏障的两边的所有指令都是正确的程序顺序,而保持程序顺序的外部可见性;(处理器指令冲排序)

     2、其次它们可以实现内存数据可见性,确保内存数据会同步到CPU缓存子系统。(内存重排序)

Store Barrier

Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存。这会使得程序状态对其它CPU可见,这样其它CPU可以根据需要介入。一个实际的好例子是Disruptor中的BatchEventProcessor。当序列Sequence被一个消费者更新时,其它消费者(Consumers)和生产者(Producers)知道该消费者的进度,因此可以采取合适的动作。所以屏障之前发生的内存更新都可见了。

01 private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE;
02 // from inside the run() method
03 T event = null;
04 long nextSequence = sequence.get() + 1L;
05 while (running)
06 {
07     try
08     {
09         final long availableSequence = barrier.waitFor(nextSequence);
10         while (nextSequence <= availableSequence)
11         {
12             event = ringBuffer.get(nextSequence);
13             boolean endOfBatch = nextSequence == availableSequence;
14             eventHandler.onEvent(event, nextSequence, endOfBatch);
15             nextSequence++;
16         }
17         sequence.set(nextSequence - 1L);
18         // store barrier inserted here !!!
19     }
20     catch (final Exception ex)
21     {
22         exceptionHandler.handle(ex, nextSequence, event);
23         sequence.set(nextSequence);
24         // store barrier inserted here !!!
25         nextSequence++;
26     }
27 }

Load Barrier

Load屏障,是x86上的”ifence“指令,强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令。这使得从其它CPU暴露出来的程序状态对该CPU可见,这之后CPU可以进行后续处理。一个好例子是上面的BatchEventProcessor的sequence对象是放在屏障后被生产者或消费者使用。

Full Barrier

Full屏障,是x86上的”mfence“指令,复合了load和save屏障的功能。

Java内存模型

Java内存模型volatile变量在写操作之后会插入一个store屏障,在读操作之前会插入一个load屏障。一个类的final字段会在初始化后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见。

原子指令和Software Locks

原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。

posted @ 2016-07-07 09:49  YDDMAX  阅读(372)  评论(0编辑  收藏  举报