2017-06-04 19 views
3

配列を宣言した場合、配列への参照はvolatileであることはわかっています。volatile揮発性配列の奇妙な振る舞い

私はミューテックスアルゴリズムを学んでいますので、私はいくつかのテストコードを書く:フィルタアルゴリズムの私の最初の実装では

public class MutualExclusion { 
    static final int N = 10; 
    static final int M = 100000; 

    volatile static int count = 0; 

    public static void main(String[] args) { 
     Thread[] threads = new Thread[N]; 
     for (int i = 0; i < N; i++) { 
      Thread t = new Worker(i); 
      threads[i] = t; 
      t.start(); 
     } 
     for (Thread t: threads) { 
      try { 
       t.join(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
     if (count != N * M) { 
      System.out.println("count (" + count + ") != N * M (" + String.valueOf(N * M) + ")"); 
     } 
    } 

    static class Worker extends Thread { 
     int id; 
     Worker(int id) { 
      this.id = id; 
     } 

     @Override 
     public void run() { 
      for (int i = 0; i < M; i++) { 
       this.lock(); 
       // critical section 
       count++; 
       if (i % 1000 == 0) { 
        System.out.println(this.getName() + ": " + count); 
       } 
       this.unlock(); 
      } 
     } 


     void lock() { 
      filterLock(); 
     } 

     void unlock() { 
      filterUnlock(); 
     } 

     static volatile int level[] = new int[N]; 
     static volatile int lastToEnter[] = new int[N - 1]; 

     void filterLock() { 
      for (int i = 0; i < (N - 1); i++) { 
       level[this.id] = i; 
       lastToEnter[i] = this.id; 
       outer: 
       while (lastToEnter[i] == this.id) { 
        for (int k = 0; k < N; k++) { 
         if (k != this.id && level[k] >= i) { 
          continue outer; 
         } 
        } 
        break; 
       } 
      } 
     } 

     void filterUnlock() { 
      level[this.id] = -1; 
     } 
    } 
} 

を、私は変数levellastToEnterためvolatileを逃し、驚くことではないが、プログラムが無限に入りましたループ。紛失したvolatileを追加した後、プログラムは期待どおりに終了することができます。

私が最初に言ったように、volatile配列は、配列内の項目がvolatileであるということを意味しません。

volatileというキーワードを追加した後でも正しく動作しない別のミューテックスアルゴリズムを実装していたときに、私はこの質問をしました。私は配列は揮発性であることのように見えるでアイテムを作るためにトリック(Java volatile array?)を使用する必要があります - しかし、もし

volatile static boolean[] b = new boolean[N]; 
volatile static boolean[] c = new boolean[N]; 
volatile static int k = 0; 

void dijkstraLock() { 
    b[this.id] = false; 
    outer: 
    for (;;) { 
     if (k == this.id) { 
      c[this.id] = false; 

      c = c; // IMPORTANT! the trick 

      for (int i = 0; i < N; i++) { 
       if (i != this.id && !c[i]) { 
        continue outer; 
       } 
      } 
      break; 
     } else { 
      c[this.id] = true; 
      if (b[k]) { 
       k = this.id; 
      } 
     } 
    } 
} 

void dijkstraUnlock() { 
    b[this.id] = true; 
    c[this.id] = true; 
} 
揮発性元素を含まないJavaで
+0

あなたはただ運が良いと思っていますか?コードが正しく1回実行されるか、特定のマシンで常に正しく実行されるという理由だけで、常に正しく動作することが保証されているわけではありません。 Dijkstraのロックコードとの1つの違いは、メインループのvolatile int 'count'に書き込んでいることです。これはループの繰り返しごとにスレッド間の瞬間的な出来事前の関係を作成します(' count'を間違って更新しています'count ++'はアトミックではないので) –

+0

@ErwinBolwidt私はまた、コードが正しく動作するのは単なる運が良いと思っていましたが、コードを複数回実行してこれを確認します。 'volatile'がなければ、' filterLock'は 'volatile'キーワードの状況とは非常に異なる実行の直後に無限ループに入ります。ミューテックスアルゴリズム(正しく実装されている場合)がクリティカルセクションコードを実行しているスレッドが1つしかないことを保証できるため、 'count ++ 'を使用することを意図しています。 – Yon

答えて

1

揮発性アレイ(以下のコードは、直接Workerクラスに貼り付けることができます)あなたは、揮発性の配列参照を介してそれらにアクセスすると、あなたは揮発性の読み取りを取得します。例えば、上記のコードで:

lastToEnter[i] = this.id; 

ではないのに対し、

static volatile int lastToEnter[] = new int[N - 1]; 

は、揮発書き込みです。しかし、配列値の評価 - など:

lastToEnter[i] == this.id 

はを読んで揮発性です - あなたが最初に揮発性である配列への参照を読んで、そして唯一のそのを評価するために、i番目の要素にアクセスします値。

これは、配列がvolatileと宣言されたときに実行が成功した原因と思われます。

+2

なぜ揮発性読み出しが特別なのですか? OPが投稿したdijkstraロックもvolatile読み取りを使用しますが、 'c = c; 'トリックがなければ動作しません。揮発性の読み取りは、Javaのメモリモデルには何の影響もありません。揮発性の読み取りが別のスレッドによってvolatile * write *に続く場合は、2つのスレッドの間に先起こりの関係が設定されます。その中の要素に。 –

+0

私はあなたのことを理解しています。一連の揮発性の読み込み - スレッドがコード全体で揮発性の読み込みで配列にアクセスするので - スレッドに何らかの関係が強制されないことをお勧めしますか? – Assafs

+1

はJavaメモリモデルに準拠していません。事実、JVMの実際の実装はそれほど厳密ではない可能性があるため、JMMによって正しく同期されていない場合でも、一部のコードが正しく実行される可能性があります。しかし、JVMの次のバージョンが登場するか、Intelは新しいJVMの使用のための素敵な新機能を並列化のために提供しています。 –