关于伪共享这个概念,请先参照http://ifeve.com/falsesharing/
伪共享的样子:
class="java" name="code">Java view:
public class VolatileLong {
volatile long v = 0L;
}
Memory view:
...–––-)(––––HV––––HV–––)(–––...
我们看到,两个VolatileLong对象被load到了同一个缓存行里面,如果一个
线程要修改对象1,另一个线程同时要修改对象2,此时就要面对伪共享这个无形的性能杀手了
jdk6中的解决办法:
Java view:
public class VolatileLong {
volatile long p0, p1, p2, p3, p4, p5, p6;
volatile long v = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
}
Memory view:
...–––-)(ppppppHVqqqqqq)(–––...
很多大神的代码都是通过上面的方式也就是long padding来避免伪共享
例如:
1.Doug Lea的jsr166中早期的LinkedTransferQueue
版本http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/LinkedTransferQueue.java?revision=1.1&view=markup
2.还是Doug Lea的ConcurrentHashMapV8http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.121&view=markup(在java8中的版本已经用sun.misc.Contended替换long padding)
3.大名鼎鼎的无锁并发框架Disruptor https://github.com/LMAX-Exchange/disruptor
4.等等等...
long padding的解决办法不怎么优雅,并且在jdk7某个版本以后能会优化掉long padding,尼玛java
程序员是有多难啊,想尽了一切办法对付jdk7中的这个优化,详情点击http://ifeve.com/false-sharing-java-7/
但是但是...开始使用jdk8的同学注意了,java8已经给出了官方的解决办法...
就是sun.misc.Contended
注解http://mail.openjdk.java.net/pipermail/
hotspot-dev/2012-November/007309.html
Java view:
// jdk8新特性,Contended注解避免false sharing
// Restricted on user classpath
// Unlock: -XX:-RestrictContended
@sun.misc.Contended
public class VolatileLong {
volatile long v = 0L;
}
Memory view:
...–––-)(******HV******)(–––...
要注意的是user classpath使用此注解默认是无效的,需要在jvm启动时设置-XX:-RestrictContended
jdk8中已经使用sun.misc.Contended的地方:
src/share/classes/java/util/concurrent/ConcurrentHashMap.java
2458: @sun.misc.Contended static final class CounterCell {
src/share/classes/java/util/concurrent/Exchanger.java
310: * bookkeeping. Padded via @sun.misc.Contended to reduce m
313: @sun.misc.Contended static final class Node {
src/share/classes/java/util/concurrent/ForkJoinPool.java 161:@sun.misc.Contended
643: @sun.misc.Contended
src/share/classes/java/util/concurrent/atomic/Striped64.java
55: * (via @sun.misc.Contended) to reduce cache contention. Pa
119: @sun.misc.Contended static final class Cell {
src/share/classes/java/lang/Thread.java
2004: @sun.misc.Contended("tlr")
2008: @sun.misc.Contended("tlr")
2012: @sun.misc.Contended("tlr")
最后,贴上测试代码,感兴趣的各自测试吧
public class FalseSharing implements Runnable {
public final static int NUM_THREADS = 4; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static VolatileLong3[] longs = new VolatileLong3[NUM_THREADS];
static {
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong3();
}
}
public FalseSharing(final int arrayIndex) {
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception {
long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() - start));
}
private static void runTest() throws InterruptedException {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
}
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong {
public volatile long value = 0L;
}
// long padding避免false sharing
// 按理说jdk7以后long padding应该被优化掉了,但是从测试结果看padding仍然起作用
public final static class VolatileLong2 {
volatile long p0, p1, p2, p3, p4, p5, p6;
public volatile long value = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
}
// jdk8新特性,Contended注解避免false sharing
// Restricted on user classpath
// Unlock: -XX:-RestrictContended
@sun.misc.Contended
public final static class VolatileLong3 {
public volatile long value = 0L;
}
}
我机器上的测试结果:
VolatileLong: duration = 29594765000
VolatileLong2:duration = 7234555000
VolatileLong3:duration = 7167475000
测试代码来自http://ifeve.com/falsesharing/,稍有改动