轉自:http://budairenqin.iteye.com/blog/2048257
關於僞共享這個概念,請先參照http://ifeve.com/falsesharing/
僞共享的樣子:
- 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/,稍有改動