2016-08-14 13 views
5

変数を変更したjavaと同期メソッドでマルチスレッドコードを書きましたが、コードが同期していないため、乱数値が得られます。私のコードがあります:このマルチスレッドプログラムで同期メソッドが同期してアクセスされないのはなぜですか?

public class Main { 
    public static void main(String[] args) throws Exception { 
     Resource.i = 5; 
     MyThread myThread = new MyThread(); 
     myThread.setName("one"); 
     MyThread myThread2 = new MyThread(); 
     myThread.start(); 
     myThread2.start(); 
     myThread.join(); 
     myThread2.join(); 
     System.out.println(Resource.i); 
    } 
} 
class MyThread extends Thread { 
    @Override 
    public void run() { 
     synMethod(); 
    } 

    private synchronized void synMethod() { 
     int i = Resource.i; 
     if(Thread.currentThread().getName().equals("one")) { 
      Thread.yield(); 
     } 
     i++; 
     Resource.i = i; 
    } 
} 

class Resource { 
    static int i; 
} 

私はいくつかの他のスレッドがこれを実行中の操作はアトミックでなければなりませんので、何のスレッドが、この方法で行くべきではありません理解と時々、私は時々、6、7を得るが、私はsynMethodを同期しました、しかし、彼らはそうではありません、なぜ私は理解できませんか?それを私に説明して答えてください - どうすれば修正できますか?

+2

。サブクラスのスレッドではないことをお勧めします。これは驚くべき結果をもたらす可能性があるためです。 –

答えて

10

​​メソッドを追加するのは、thisで同期するのと同じです。 2つのスレッドのインスタンスがあるため、互いにロックされず、この同期は実際には何もしません。

同期を有効にするには、共有リソースを同期する必要があります。あなたの例では、Resource.classは良い選択で可能性:

private void synMethod() { // Not defined as synchronized 
    // Synchronization done here: 
    synchronized (Resource.class) { 
     int i = Resource.i; 
     if (Thread.currentThread().getName().equals("one")) { 
      Thread.yield(); 
     } 
     i++; 
     Resource.i = i; 
    } 
} 
+0

Resource.iへの読み取りアクセスは、更新プログラムの適切な可視性を確保するためにも同期化する必要があります。 –

+0

@ J.Bあなたは、両方のスレッドで 'Resource.i' _after_' join() 'ingを読むことを意味しますか?さて、 'join()'は[起こる前の関係]を作成します(https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5)(_All他のスレッドがそのスレッド上のjoin()から正常に戻る前に、スレッド内のアクションが発生し、その部分が正しいようになります。 – Roman

1

のは、Oracleのマニュアルページからsynchronized methodsの定義を見てみましょう。二つの効果がある​​方法を作る

まず、それがインターリーブする同じオブジェクトの同期メソッドの2つの呼び出しは可能ではありません。あるスレッドがオブジェクトの同期メソッドを実行しているとき、そのオブジェクトで最初のスレッドが完了するまで、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッド(実行を中断)。あなたのクエリに戻って来る

synMethod()は同期メソッドのオブジェクトレベルです。同じ​​メソッドにアクセスする2つのスレッドがオブジェクトロックを順次取得します。しかし、異なるインスタンス(オブジェクト)の同期メソッドにアクセスする2つのスレッドは、共有ロックがない場合に非同期に実行されます。

myThreadmyThread2は2つの異なるオブジェクト=>本質的なロックは2つの異なるオブジェクトで取得されるため、これらのメソッドに非同期でアクセスできます。

1つの解決策:Mureinikで引用されているように、共有オブジェクトを使用してロックします。

その他の溶液(S):などReentrantLock

あなたは、関連するSEの問題のいくつかのより多くの選択肢を見つけるように、より良い並行処理の構文を使用してください:あなたは、同期の問題でロック何

Avoid synchronized(this) in Java?

関連する問題