-2
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Main implements Runnable {

   private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
   private volatile int bar = 0;
   private AtomicInteger count = new AtomicInteger(0);

   private static final int NUM_THREADS = 25;

   public static void main(String[] args) {
      Main main = new Main();
      for(int i = 0; i < NUM_THREADS; i++)
         new Thread(main).start();
   }

   public void run() {
      int i = count.incrementAndGet();
      cdl1.countDown();
      try {
         cdl1.await();
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }
      bar = i;
      if(bar != i)
         System.out.println("Bar not equal to i");
      else
         System.out.println("Bar equal to i");
   }

}

それぞれThreadが run メソッドに入り、呼び出された から値を取得することによって、スレッドに限定された一意のint変数を取得します。次に、それぞれが呼び出されるのを待ちます(最後のものがラッチに到達すると、すべてが解放されます)。ラッチが解放されると、各スレッドは、限定された値を共有されたに割り当てようとします。iAtomicIntegercountThreadCountDownLatchcdl1ThreadThreadsivolatileintbar

Thread1つを除くすべてが「バーがiに等しくない」と出力することを期待しますが、すべてThreadが「バーがiに等しい」と出力します。ええと、wtf はvolatile実際にそうしないとしますか?

それぞれがまったく同時にThreadの値を設定しようとするのは、意図的な意図です。bar

編集:

答えに照らして、コードを次のように変更しました。

...
bar = i;
try {
    Thread.sleep(0);
} catch(InterruptedException e) {
    e.printStackTrace();
}
...

変数の設定と読み取りの間に少しの時間が無駄になるようにするため。

これで、バーの同じ/異なる値で印刷が 50/50 になりました。

4

5 に答える 5

8

スレッドの実行時期を決定するのは JVM であり、ユーザーではありません。ラッチがリリースされたばかりの 1 つをさらに 10 ミリ秒保持したい場合は、それが可能です。ラッチが解除された後も、順番が実行されるまで待機する必要があります。25 コアのコンピューターで実行している場合を除き、マシン内でほぼ「同時に」バーを割り当てているわけではありません。実行しているのはいくつかの基本的な操作だけなので、そのうちの 1 つが次の操作がリリースされる前にタイム スライス内で終了しない可能性は非常に低いです。

于 2010-05-07T23:49:38.107 に答える
2

そうではありません。あなたはそれを悪用しています。Herb Sutter による素晴らしい記事がここにあり、それをより詳細に説明しています。

基本的な考え方は、volatile変数を最適化できないようにすることです。スレッドセーフにはなりません。

于 2010-05-08T00:01:31.063 に答える
2

「WTF は実際に volatile を実行しますか?」に答えるには:

volatile は可視性がすべてです。Java のスレッド モデルでは、スレッド A が通常の共有フィールドに書き込みを行った場合、スレッドが何らかの形で同期されない限り、スレッド B が A によって書き込まれた値を参照できるという保証はありませんvolatile は、同期メカニズムの 1 つです。

不揮発性フィールドとは異なり、スレッド A が揮発性フィールドに書き込み、後でスレッド B がそれを読み取る場合、B は古いバージョンではなく新しい値を確認することが保証されます。

(実際には volatile はさらに多くのことを行います - スレッド B はフィールドの新しい値を見るだけでなく、volatile 変数を設定する前に A によって書き込まれた他のすべてのものも見ることができます。それは発生前の関係を確立しました)。

于 2010-05-08T00:05:20.057 に答える
1

あなたがすべきことは、 のインスタンスを に置き換えることvolatile intですAtomicIntegerここを参照してください。

于 2010-05-08T00:01:59.813 に答える
0

私はあなたがこれを書くつもりだったと思います:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Main implements Runnable {

   private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
   private volatile int bar = 0;
   private AtomicInteger count = new AtomicInteger(0);

   private static final int NUM_THREADS = 25;

   public static void main(String[] args) {
      Main main = new Main();
      for(int i = 0; i < NUM_THREADS; i++)
         new Thread(main).start();
   }

   public void run() {
      int i = count.incrementAndGet();
      bar = i;
      cdl1.countDown();
      try {
         cdl1.await();
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }
      if(bar != i)
         System.out.println("Bar not equal to i");
      else
         System.out.println("Bar equal to i");
   }

}

あなたが期待したように、「Bar not equal to i」を出力します。

于 2010-05-07T23:49:12.320 に答える