Java 7与伪共享的新仇旧恨

原文:False Shareing && Java 7 (依然是马丁的博客)  译者:杨帆 校对:方腾飞

在我前一篇有关伪共享的博文中,我提到了可以加入闲置的long字段来填充缓存行来避免伪共享。但是看起来Java 7变得更加智慧了,它淘汰或者是重新排列了无用的字段,这样我们之前的办法在Java 7下就不奏效了,但是伪共享依然会发生。我在不同的平台上实验了一些列不同的方案,并且最终发现下面的代码是最可靠的。(译者注:下面的是最终版本,马丁在大家的帮助下修改了几次代码)

查看源代码

打印帮助

01 import java.util.concurrent.atomic.AtomicLong;
02  
03 public final class FalseSharing
04     implements Runnable
05 {
06     public final static int NUM_THREADS = 4; // change
07     public final static long ITERATIONS = 500L * 1000L * 1000L;
08     private final int arrayIndex;
09  
10     private static PaddedAtomicLong[] longs = new PaddedAtomicLong[NUM_THREADS];
11     static
12     {
13         for (int i = 0; i < longs.length; i++)
14         {
15             longs[i] = new PaddedAtomicLong();
16         }
17     }
18  
19     public FalseSharing(final int arrayIndex)
20     {
21         this.arrayIndex = arrayIndex;
22     }
23  
24     public static void main(final String[] args) throws Exception
25     {
26         final long start = System.nanoTime();
27         runTest();
28         System.out.println("duration = " + (System.nanoTime() - start));
29     }
30  
31     private static void runTest() throws InterruptedException
32     {
33         Thread[] threads = new Thread[NUM_THREADS];
34  
35         for (int i = 0; i < threads.length; i++)
36         {
37             threads[i] = new Thread(new FalseSharing(i));
38         }
39  
40         for (Thread t : threads)
41         {
42             t.start();
43         }
44  
45         for (Thread t : threads)
46         {
47             t.join();
48         }
49     }
50  
51     public void run()
52     {
53         long i = ITERATIONS + 1;
54         while (0 != --i)
55         {
56             longs[arrayIndex].set(i);
57         }
58     }
59  
60     // 这段代码的来历可以看4楼的回复
61     public static long sumPaddingToPreventOptimisation(final int index)
62     {
63         PaddedAtomicLong v = longs[index];
64         return v.p1 + v.p2 + v.p3 + v.p4 + v.p5 + v.p6;
65     }
66  
67     public static class PaddedAtomicLong extends AtomicLong
68     {
69         public volatile long p1, p2, p3, p4, p5, p6 = 7L;
70     }
71 }

用以上这种办法我获得了和上一篇博客里提到的相近的性能,读者可以把PaddedAtomicLong里面那行填充物注释掉再跑测试看看效果。

我想我们大家都有权去跟Oracle投诉,让他们在JDK里默认加入缓存行对齐的函数或者是被填充好的原子类型,这和其他一些底层改变会让Java成为一门真真正正的并发编程语言。我们一直以来不断的在听到他们讲多核时代正在到来,但是我要说的是在这方面Java需要快点赶上来。

时间: 2024-09-20 14:30:31

Java 7与伪共享的新仇旧恨的相关文章

Java中的伪共享以及应对方案

什么是伪共享 CPU缓存系统中是以缓存行(cache line)为单位存储的.目前主流的CPU Cache的Cache Line大小都是64Bytes.在多线程情况下,如果需要修改"共享同一个缓存行的变量",就会无意中影响彼此的性能,这就是伪共享(False Sharing). CPU的三级缓存 由于CPU的速度远远大于内存速度,所以CPU设计者们就给CPU加上了缓存(CPU Cache). 以免运算被内存速度拖累.(就像我们写代码把共享数据做Cache不想被DB存取速度拖累一样),C

从Java视角理解伪共享(False Sharing)

作者:coderplay 从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态从我的前一篇博文中, 我们知道了CPU缓存及缓存行的概念, 同时用一个例子说明了编写单线程Java代码时应该注意的问题. 下面我们讨论更为复杂, 而且更符合现实情况的多核编程时将会碰到的问题. 这些问题更容易犯, 连j.u.c包作者Doug Lea大师的JDK代码里也存在这些问题.MESI协议及RFO请求从前一篇我们知道, 典型的CPU微架构有3级缓存, 每个核都有自己私有的L1, L2缓存. 那么多线程

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

从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态 从我的前一篇博文中, 我们知道了CPU缓存及缓存行的概念, 同时用一个例子说明了编写单线程Java代码时应该注意的问题. 下面我们讨论更为复杂, 而且更符合现实情况的多核编程时将会碰到的问题. 这些问题更容易犯, 连j.u.c包作者Doug Lea大师的JDK代码里也存在这些问题. MESI协议及RFO请求 从前一篇我们知道, 典型的CPU微架构有3级缓存, 每个核都有自己私有的L1, L2缓存. 那么多线程编程时, 另外一个核

伪共享(False Sharing)

原文地址:http://ifeve.com/false-sharing/ 作者:Martin Thompson  译者:丁一 缓存系统中是以缓存行(cache line)为单位存储的.缓存行是2的整数幂个连续字节,一般为32-256个字节.最常见的缓存行大小是64个字节.当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享.缓存行上的写竞争是运行在SMP系统中并行线程实现可伸缩性最重要的限制因素.有人将伪共享描述成无声的性能杀手,因为从代码中很难看清

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

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

伪共享和缓存行

题图:计算机主板图,授权基于CC0 作者:李艳鹏 编辑:王友强 在计算机系统中,内存是以缓存行为单位存储的,一个缓存行存储字节的数量为2的倍数,在不同的机器上,缓存行大小为32字节到256字节不等,通常来说为64字节.伪共享指的是在多个线程同时读写同一个缓存行的不同变量的时候,尽管这些变量之间没有任何关系,但是在多个线程之间仍然需要同步,从而导致性能下降的情况.在对称多处理器结构的系统中,伪共享是影响性能的主要因素之一,由于很难通过走查代码的方式定位伪共享的问题,因此,大家把伪共享称为"性能杀手

远程操作access数据库-java操作其它机器共享的access数据库

问题描述 java操作其它机器共享的access数据库 做插入操作的时候报错:java.sql.SQLException [microsoft] [ODBC Microsoft Access Driver] 操作必须使用一个可更新的查询

java基础多线程之共享数据

java基础巩固笔记5-多线程之共享数据 线程范围内共享数据 ThreadLocal类 多线程访问共享数据 几种方式 本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的数据不交叉:一是多个线程间如何共享数据,保证数据的一致性. 线程范围内共享数据 自己实现的话,是定义一个Map,线程为键,数据为值,表中的每一项即是为每个线程准备的数据,这样在一个线程中数据是一致的. 例子 package com.iot.thread; import java.util

java多线程通信之共享数据

(1)当访问共同的代码的时候:可以使用同一个Runnable对象,这个Runnable对象中有这个共享数据,比如卖票系统就可以这么做. public class MutiThreadShareData {     public static void main(String[] args) {                   ShareData data=new ShareData();                   new Thread(data).start();