2011-08-12 27 views
2

如果int遞增/遞減操作在Java 6中不是原子的,也就是說,它們被分成幾步執行(讀取值,增量,寫入等),我希望看到一段代碼多個線程如何影響單個int變量,從而完全破壞整個變量。如何模擬一個情況,當i ++被同時執行的線程破壞?

例如,基本步驟包括但不包括所有這些: i ++〜=將i放入寄存器;增加i(增加操作);寫回我的記憶;

如果兩個或兩個以上的線程在進程中交錯,那可能意味着對i ++的兩次後續調用後的值將只增加一次。

你可以演示一段代碼在java中,模擬多線程環境中的這種情況?

+0

如果您在某些實際程序中需要此選項,那麼只需對線程之間共享的任何數據的訪問進行同步即可。如果你需要它進行一些研究,那麼我無法幫助。 – Zds

+0

根據http://stackoverflow.com/questions/1006655是'int'操作原子(而'例如'長'操作*可*是原子的) –

+1

'int'讀/寫操作是原子的,但是'++'是不是原子的。 –

回答

1

這是代碼。很抱歉的static,只是想節省幾行代碼,它不會影響結果:

public class Test 
{ 
    public static int value; 

    public static void main(String[] args) throws InterruptedException 
    { 
     Runnable r = new Runnable() { 
      @Override 
      public void run() { 
       for(int i = 0; i < 50000; ++i) 
        ++value; 
      } 
     }; 

     List<Thread> threads = new ArrayList<Thread>(); 
     for(int j = 0; j < 2; ++j) 
      threads.add(new Thread(r)); 
     for (Thread thread : threads) 
      thread.start(); 
     for (Thread thread : threads) 
      thread.join(); 

     System.out.println(value); 
    } 
} 

這個程序可以50000和100000之間的任何打印,但它從來沒有真正在我的機器上印刷10萬。

現在用AtomicIntegerincrementAndGet()方法替代int方法。它將始終打印100000,而不會對性能造成太大影響(它使用CPU CAS指令,不支持Java同步)。

+0

你提供的很好的例子。:) – EugeneP

+0

您可以打印您看到的結果,因爲第一個線程可以在第二個線程開始之前完成。 (請參閱我的回答) –

+0

有趣的是,如果您在一個塊中加入兩個循環(start(),join()),您將得到完全相反的效果,計數器值將與預期一致。 – EugeneP

3
public class Test { 
    private static int counter; 

    public static void main(String[] args) throws InterruptedException { 
     Runnable r = new Runnable() { 
      public void run() { 
       for (int i = 0; i < 100000; i++) { 
        counter++; 
       } 
      } 
     }; 
     Thread t1 = new Thread(r); 
     Thread t2 = new Thread(r); 
     t1.start(); 
     t2.start(); 
     t1.join(); 
     t2.join(); 
     if (counter != 200000) { 
      System.out.println("Houston, we have a synchronization problem: counter should be 200000, but it is " + counter); 
     } 
    } 
} 

運行我的機器上這個節目給

Houston, we have a synchronization problem: counter should be 200000, but it is 198459 
+0

也是一個很好的例子。 – EugeneP

+0

您可以打印您看到的結果,因爲第一個線程可以在第二個線程啓動之前完成。 (看我的回答) –

1

你需要爲++許多重複運行測試是快速和之前有時間有是一個問題可以運行到結束。

public static void main(String... args) throws InterruptedException { 
    for (int nThreads = 1; nThreads <= 16; nThreads*=2) 
     doThreadSafeTest(nThreads); 
} 

private static void doThreadSafeTest(final int nThreads) throws InterruptedException { 
    final int count = 1000 * 1000 * 1000; 

    ExecutorService es = Executors.newFixedThreadPool(nThreads); 
    final int[] num = {0}; 
    for (int i = 0; i < nThreads; i++) 
     es.submit(new Runnable() { 
      public void run() { 
       for (int j = 0; j < count; j += nThreads) 
        num[0]++; 
      } 
     }); 
    es.shutdown(); 
    es.awaitTermination(10, TimeUnit.SECONDS); 
    System.out.printf("With %,d threads should total %,d but was %,d%n", nThreads, count, num[0]); 
} 

打印

With 1 threads should total 1,000,000,000 but was 1,000,000,000 
With 2 threads should total 1,000,000,000 but was 501,493,584 
With 4 threads should total 1,000,000,000 but was 319,482,716 
With 8 threads should total 1,000,000,000 but was 261,092,117 
With 16 threads should total 1,000,000,000 but was 202,145,371 

只有500K我有一個基本的筆記本電腦下面。在更快的機器上,您可以在出現問題之前獲得更高的迭代次數。

With 1 threads should total 500,000 but was 500,000 
With 2 threads should total 500,000 but was 500,000 
With 4 threads should total 500,000 but was 500,000 
With 8 threads should total 500,000 but was 500,000 
With 16 threads should total 500,000 but was 500,000 
+0

很好的解釋,謝謝。 – EugeneP

相關問題