2015-09-06 42 views
3

我測試了多線程Atomic Integer與比較同步方法的速度有多快,但是我得到的結果是Atomic Integer比synchronized方法慢。AtomicInteger比同步更慢

我讀了Java API Reference,我明白Atomic Integer在多線程中比同步更快。

所以我想知道爲什麼我用原子整數得到不好的結果的原因。 我的測試代碼錯了?

這裏是我的代碼

import java.util.concurrent.atomic.AtomicInteger; 

public class AtomicThreadTest { 

    // Number of thread to run 
    private static final int THREAD_NUM = 1000; 
    // Repetition number to count up 
    private static final int ROOP_NUM = 200000; 

    // Base counter class 
    private abstract class Counter implements Runnable { 
     @Override 
     public void run() { 
      for (int i = 0; i < ROOP_NUM; i++) { 
       increment(); 
      } 
     } 
     // Increment method 
     public abstract void increment(); 
     // Get result of calculation 
     public abstract int getResult(); 
     // Get name of the class 
     @Override 
     public String toString() { 
      return getClass().getSimpleName(); 
     } 
    } 

    // Use no thread safe 
    private static int count = 0; 
    private class NoThreadSafeCounter extends Counter { 
     @Override 
     public void increment() { 
      count++; 
     } 
     @Override 
     public int getResult() { 
      return count; 
     } 
    } 

    // Use volatile 
    private static volatile int volatileCount = 0; 
    private class VolatileCounter extends Counter { 
     @Override 
     public void increment() { 
      volatileCount++; 
     } 
     @Override 
     public int getResult() { 
      return volatileCount; 
     } 
    } 

    // Use synchronized 
    private static int synchronizedCount = 0; 
    private class SynchronizedCounter extends Counter { 
     @Override 
     public synchronized void increment() { 
      synchronizedCount++; 
     } 
     @Override 
     public int getResult() { 
      return synchronizedCount; 
     } 
    } 

    // Use AtomicInteger 
    private static AtomicInteger atomicCount = new AtomicInteger(0); 
    private class AtomicCounter extends Counter { 
     @Override 
     public void increment() { 
      atomicCount.incrementAndGet(); 
     } 
     @Override 
     public int getResult() { 
      return atomicCount.get(); 
     } 
    } 

    public static void main(String[] args) { 
     AtomicThreadTest testClass = new AtomicThreadTest(); 
     Counter[] testCounter = { 
       testClass.new NoThreadSafeCounter(), 
       testClass.new VolatileCounter(), 
       testClass.new SynchronizedCounter(), 
       testClass.new AtomicCounter(), 
     }; 

     for (Counter c : testCounter) { 
      System.out.println("-------------------------"); 
      System.out.printf("Test for class : %s\n", c.toString()); 
      try { 
       test(c); 
      } catch (InterruptedException e) { 
       System.out.println("Test halted"); 
      } finally { 
       System.out.println(""); 
       System.gc(); 
      } 
     } 
     System.out.println("-------------------------"); 
    } 

    public static void test(final Counter counter) throws InterruptedException { 
     System.out.printf("Start with threads : %d, roop : %d\n", THREAD_NUM, ROOP_NUM); 
     final long startTime = System.currentTimeMillis(); 

     // Create THREAD_NUM threads and run them 
     Thread[] threads = new Thread[THREAD_NUM]; 

     for (int i = 0; i < THREAD_NUM; i++) { 
      threads[i] = new Thread(counter); 
      threads[i].start(); 
     } 

     // Wait for all threads other than this end 
     while (Thread.activeCount() > 1) { 
      Thread.sleep(10); 
     } 

     final long endTime = System.currentTimeMillis(); 
     System.out.printf("Result %d, Expected %d\n", counter.getResult(), THREAD_NUM*ROOP_NUM); 
     System.out.printf("Time to calc : %d\n", endTime-startTime); 
    } 
} 

而且我得到了下面的結果。

------------------------- 
Test for class : NoThreadSafeCounter 
Start with threads : 1000, roop : 200000 
Result 198785583, Expected 200000000 
Time to calc : 127 

------------------------- 
Test for class : VolatileCounter 
Start with threads : 1000, roop : 200000 
Result 19162116, Expected 200000000 
Time to calc : 4458 

------------------------- 
Test for class : SynchronizedCounter 
Start with threads : 1000, roop : 200000 
Result 200000000, Expected 200000000 
Time to calc : 8426 

------------------------- 
Test for class : AtomicCounter 
Start with threads : 1000, roop : 200000 
Result 200000000, Expected 200000000 
Time to calc : 15190 
+3

您不應該調用'System.gc()',因爲您不知道它何時會被執行。你沒有使用大量的內存,在執行期間你不太可能需要GC – Dici

+2

順便說一下,你的同步(最後的while循環)很糟糕,並且消耗了大量的CPU。你應該使用類似信號量的東西 – Dici

+0

@Dici謝謝你的回覆。對於system.gc(),我再次讀取我的代碼並瞭解每個Counter對象非常簡單,並且消耗的內存很少。所以,我不需要調用'System.gc()'。這樣對嗎? – user2738844

回答

1

儘管測試用例存在代碼問題,但我們先來討論一下多線程問題本身。

如果您將THREAD_NUM設置爲更低的數字,例如8或4,您會發現AtomicCounterSynchronizedCounter要快一些。使用1000個線程運行將會在AtomicInteger的CAS(比較和交換)上花費兩個CPU,導致其運行速度比​​代碼塊慢。

爲了證明這一點,你可以實現一個LongadderCounter

private class LongadderCounter extends Counter { 

    @Override 
    public void increment() { 
     longadder.increment(); 
    } 

    @Override 
    public int getResult() { 
     return longadder.intValue(); 
    } 
} 

你會發現,LongadderCounter快得多。 LongAdderAtomicIntegerConcurrentHashMapCollections.synchronizedMap(new HashMap<>()),它將計數器分成多個部分,並在每個部分進行CAS以減輕競爭條件。

+0

我對LongAdder的速度感到驚訝!它花了1/15的時間與AtomicInteger。並感謝您的禮貌解釋! – user2738844