2016-09-28 13 views
1

変数のvolatileキーワードとstaticキーワードの違いを理解しました。静的な揮発性キーワードが期待どおりに機能しない

静的変数は異なるインスタンスによって変更できますが、揮発性変数は異なるスレッドによって変更できます。

しかし、MY_INT変数の静的キーワードを削除すると、以下のプログラム(インターネットからコピーされ、ほとんど変更されません)がハングします。

変数MY_INTへの更新は、静的キーワードがなくても他のスレッドで参照する必要があります。しかし、私は静的を削除する場合はハングアップします。

この問題を理解してください。

public class PrintOddAndEven extends Thread { 
    static volatile int i = 1; 

    Object lock; 

    PrintOddAndEven(Object lock) { 
     this.lock = lock; 
    } 

    public static void main(String ar[]) { 
     Object obj = new Object(); 
     PrintOddAndEven odd = new PrintOddAndEven(obj); 
     PrintOddAndEven even = new PrintOddAndEven(obj); 
     odd.setName("Odd"); 
     even.setName("Even"); 
     odd.start(); 
     even.start(); 
    } 

    @Override 
    public void run() { 
     while (i <= 10) { 
      if (i % 2 == 0 && Thread.currentThread().getName().equals("Even")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + i); 
        i++; 
        lock.notify(); 
       } 
      } 
      if (i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + i); 
        i++; 

        try { 
         lock.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
    } 
} 
+1

[Javaでvolatileキーワードを使用していますか?](http://stackoverflow.com/questions/106591/do-youever-use-the-volatile-keyword-in-java) – xenteros

+0

@ xenterosいいえ、これは 'static'の意味を尋ねる質問の複製です。 –

答えて

1

あなたのバグは、フィールドiからstaticキーワードを削除する場合は、あなたが持っているだろうという事実によるものである1 異なるフィールドインスタンスごとiので、ここであなたが2のインスタンスを持っているようPrintOddAndEven、あなた2つの異なるフィールドiを持つように、Evenスレッドは、そのiが決して変更されず、同じ理由で永久にスレッドが終了するので永久にループします。フィールドをstaticと宣言すると、スレッドは同じフィールドiを共有します。

あなたは次のように、あなたのカウンタを保持し、あなたがPrintOddAndEvenインスタンス間で共有することになりますオブジェクトモニターとしてそのインスタンスを使用する専用のクラスを作成する必要があります。

public class MyClass { 
    volatile int i = 1; 
} 

public class PrintOddAndEven extends Thread { 

    MyClass lock; 

    PrintOddAndEven(MyClass lock) { 
     this.lock = lock; 
    } 

    public static void main(String[] args) throws Exception { 
     MyClass obj = new MyClass(); 
     PrintOddAndEven odd = new PrintOddAndEven(obj); 
     PrintOddAndEven even = new PrintOddAndEven(obj); 
     odd.setName("Odd"); 
     even.setName("Even"); 
     odd.start(); 
     even.start(); 
    } 

    @Override 
    public void run() { 
     while (lock.i <= 10) { 
      if (lock.i % 2 == 0 && Thread.currentThread().getName().equals("Even")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
        lock.i++; 
        lock.notify(); 
       } 
      } 
      if (lock.i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
        lock.i++; 

        try { 
         lock.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
    } 
} 

あなたはを持っている場合カウンタのみを使用する場合は、カウンタおよびオブジェクトモニタとしてクラスAtomicIntegerのインスタンスを使用することもできます。カウンタはnew AtomicInteger(1)を使用してカウンタを1に初期化し、次にget()を使用して現在の値を取得し、incrementAndGet()を使用してカウンタをインクリメントすることを除いて、コードは上記と同じです。

+0

ありがとうございます。わかった!! –

1

このステートメントからStaticキーワードを削除すると、2つのPrintOddAndEven oddおよびevenスレッドオブジェクトが作成されました。volatile int i = 1;はクラスレベルのままではないので、すべてのスレッドオブジェクトは独自のiのコピーを持っています。奇数スレッドが実行すると、odd.i ++が更新されました。しかし、even.iは1のままであり、条件は渡されず、スレッドが静的な状態でハングしています。

public class PrintOddAndEven extends Thread { 
//static volatile int i = 1; 

Lock lock; 

PrintOddAndEven(Lock lock) { 
    this.lock = lock; 
} 


static class Lock { 
    volatile int i = 1; 
} 
public static void main(String ar[]) { 
    Lock obj = new lock(); 
    PrintOddAndEven odd = new PrintOddAndEven(obj); 
    PrintOddAndEven even = new PrintOddAndEven(obj); 
    odd.setName("Odd"); 
    even.setName("Even"); 
    odd.start(); 
    even.start(); 
} 

@Override 
public void run() { 
    while (lock.i <= 10) { 
     if (lock.i % 2 == 0 && Thread.currentThread().getName().equals("Even")) { 
      synchronized (lock) { 
       System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
       lock.i++; 
       lock.notify(); 
      } 
     } 
     if (lock.i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) { 
      synchronized (lock) { 
       System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
       lock.i++; 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
}} 

この方法では、両方のスレッド間ロックを共有することができます。

関連する問題