Java8中用sun.misc.Contended避免僞共享(false sharing)

轉自:http://budairenqin.iteye.com/blog/2048257

關於僞共享這個概念,請先參照http://ifeve.com/falsesharing/ 


僞共享的樣子: 
Java代碼  收藏代碼
  1. Java view:  
  2. public class VolatileLong {  
  3.         volatile long v = 0L;  
  4. }  
  5.   
  6. Memory view:  
  7. ...–––-)(––––HV––––HV–––)(–––...  

我們看到,兩個VolatileLong對象被load到了同一個緩存行裏面,如果一個線程要修改對象1,另一個線程同時要修改對象2,此時就要面對僞共享這個無形的性能殺手了 

jdk6中的解決辦法: 
Java代碼  收藏代碼
  1. Java view:  
  2. public class VolatileLong {  
  3.         volatile long p0, p1, p2, p3, p4, p5, p6;  
  4.         volatile long v = 0L;  
  5.         volatile long q0, q1, q2, q3, q4, q5, q6;  
  6. }  
  7.   
  8. Memory view:  
  9. ...–––-)(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代碼  收藏代碼
  1. Java view:  
  2. // jdk8新特性,Contended註解避免false sharing  
  3. // Restricted on user classpath  
  4. // Unlock: -XX:-RestrictContended  
  5. @sun.misc.Contended  
  6. public class VolatileLong {  
  7.         volatile long v = 0L;  
  8. }  
  9.   
  10.   
  11. Memory view:  
  12. ...–––-)(******HV******)(–––...  

要注意的是user classpath使用此註解默認是無效的,需要在jvm啓動時設置-XX:-RestrictContended 

jdk8中已經使用sun.misc.Contended的地方: 
Java代碼  收藏代碼
  1. src/share/classes/java/util/concurrent/ConcurrentHashMap.java  
  2. 2458@sun.misc.Contended static final class CounterCell {  
  3.   
  4. src/share/classes/java/util/concurrent/Exchanger.java  
  5. 310: * bookkeeping. Padded via @sun.misc.Contended to reduce m  
  6. 313@sun.misc.Contended static final class Node {  
  7.   
  8. src/share/classes/java/util/concurrent/ForkJoinPool.java 161:@sun.misc.Contended  
  9. 643@sun.misc.Contended  
  10.   
  11. src/share/classes/java/util/concurrent/atomic/Striped64.java  
  12. 55: * (via @sun.misc.Contended) to reduce cache contention. Pa  
  13. 119@sun.misc.Contended static final class Cell {  
  14.   
  15. src/share/classes/java/lang/Thread.java  
  16. 2004@sun.misc.Contended("tlr")  
  17. 2008@sun.misc.Contended("tlr")  
  18. 2012@sun.misc.Contended("tlr")  



最後,貼上測試代碼,感興趣的各自測試吧 
Java代碼  收藏代碼
  1. public class FalseSharing implements Runnable {  
  2.   
  3.     public final static int NUM_THREADS = 4// change  
  4.     public final static long ITERATIONS = 500L * 1000L * 1000L;  
  5.     private final int arrayIndex;  
  6.   
  7.     private static VolatileLong3[] longs = new VolatileLong3[NUM_THREADS];  
  8.     static {  
  9.         for (int i = 0; i < longs.length; i++) {  
  10.             longs[i] = new VolatileLong3();  
  11.         }  
  12.     }  
  13.   
  14.     public FalseSharing(final int arrayIndex) {  
  15.         this.arrayIndex = arrayIndex;  
  16.     }  
  17.   
  18.     public static void main(final String[] args) throws Exception {  
  19.         long start = System.nanoTime();  
  20.         runTest();  
  21.         System.out.println("duration = " + (System.nanoTime() - start));  
  22.     }  
  23.   
  24.     private static void runTest() throws InterruptedException {  
  25.         Thread[] threads = new Thread[NUM_THREADS];  
  26.   
  27.         for (int i = 0; i < threads.length; i++) {  
  28.             threads[i] = new Thread(new FalseSharing(i));  
  29.         }  
  30.   
  31.         for (Thread t : threads) {  
  32.             t.start();  
  33.         }  
  34.   
  35.         for (Thread t : threads) {  
  36.             t.join();  
  37.         }  
  38.     }  
  39.   
  40.     public void run() {  
  41.         long i = ITERATIONS + 1;  
  42.         while (0 != --i) {  
  43.             longs[arrayIndex].value = i;  
  44.         }  
  45.     }  
  46.   
  47.     public final static class VolatileLong {  
  48.         public volatile long value = 0L;  
  49.     }  
  50.   
  51.     // long padding避免false sharing  
  52.     // 按理說jdk7以後long padding應該被優化掉了,但是從測試結果看padding仍然起作用  
  53.     public final static class VolatileLong2 {  
  54.         volatile long p0, p1, p2, p3, p4, p5, p6;  
  55.         public volatile long value = 0L;  
  56.         volatile long q0, q1, q2, q3, q4, q5, q6;  
  57.     }  
  58.   
  59.     // jdk8新特性,Contended註解避免false sharing  
  60.     // Restricted on user classpath  
  61.     // Unlock: -XX:-RestrictContended  
  62.     @sun.misc.Contended  
  63.     public final static class VolatileLong3 {  
  64.         public volatile long value = 0L;  
  65.     }  
  66. }  


我機器上的測試結果: 
Java代碼  收藏代碼
  1. VolatileLong: duration = 29594765000  
  2. VolatileLong2:duration = 7234555000  
  3. VolatileLong3:duration = 7167475000  


測試代碼來自http://ifeve.com/falsesharing/,稍有改動
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章