2015-09-06 44 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