2017-04-05 14 views
1

私はスレッドの同期について把握しようとしていますが、私は遭遇している問題を理解していません。スレッドが同期しないのはなぜですか?

誰かがこれを診断するのに役立ちますか、それとも自分自身でこれを診断する方法を説明してください。

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.CyclicBarrier; 

public class Controller { 

public static void main(String[] args) {   
    int numThreads = 0; 
    List<Thread> threads = new ArrayList<>(); 

    if (args.length > 0) { 
     numThreads = Integer.parseInt(args[0]); 
    } 
    else { 
     System.out.println("No arguments"); 
     System.exit(1); 
    } 

    CyclicBarrier barrier = new CyclicBarrier(numThreads); 
    int arr[][] = new int[10][10]; 

    for (int i = 0; i < numThreads; i++) { 
     Thread newThread = new Thread(new ThreadableClass(barrier, arr)); 
     threads.add(newThread); 
    } 

    for (Thread thread : threads) { 
     thread.start(); 
    } 
    } 
} 

コマンドライン引数として必要なスレッドの数を受け入れる主な方法(上記)があります。

の大きさを想像
import java.util.concurrent.BrokenBarrierException; 
import java.util.concurrent.CyclicBarrier; 

public class ThreadableClass implements Runnable { 

private CyclicBarrier barrier; 
private int arr[][]; 

public ThreadableClass(CyclicBarrier barrier, int[][] arr) { 
    this.barrier = barrier; 
    this.arr = arr; 
} 

@Override 
public void run() { 
    long threadId = Thread.currentThread().getId(); 
    System.out.println(threadId + " Starting"); 

    for (int i = 0; i < 10; i++) { 
     changeArray(); 
     try { 
      barrier.await(); 
     } catch (InterruptedException | BrokenBarrierException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

private synchronized void changeArray() { 
    for (int i = 0; i < arr.length; i++) { 
     for (int j = 0; j < arr.length; j++) { 
      arr[i][j]++; 
     } 
    } 
    printArray(); 
} 

private synchronized void printArray() { 
    System.out.println(Thread.currentThread().getId() + " is printing: "); 
    for (int i = 0; i < arr.length; i++) { 
     for (int j = 0; j < arr.length; j++) { 
      System.out.print(arr[i][j] + " "); 
     } 
     System.out.println(); 
    } 
} 
} 

:そして、私はインクリメントに2次元配列内のすべての要素を持っているし、次のスレッドが同じことを行うにはその機会を得る前に、アレイを印刷することを目指しています作業の流れは、(下記)があります配列は2×2で、期待される出力は次のようになります:

1 1 
1 1 
2 2 
2 2 
3 3 
3 3 
4 4 
4 4 
... 
... 
(10 * numThreads)-1 (10 * numThreads)-1 
(10 * numThreads)-1 (10 * numThreads)-1 
(10 * numThreads) (10 * numThreads) 
(10 * numThreads) (10 * numThreads) 

代わりに、すべてのスレッドが配列をインクリメントし、互いの上に印刷を開始します。

+0

真剣に:これは大きな質問です。あなたはあなたのすべてのコードを置くが、そのコードはあまりにも多くない。予想された出力と実際の出力について説明しました。今すぐに受け入れるならば、素晴らしい仕事をしただけです;-) – GhostCat

答えて

4

がありません結果は驚くほどです。 nスレッドを作成します。すべてのスレッドに開始を指示します。各スレッドrun()は、以下から始まります。

long threadId = Thread.currentThread().getId(); 
System.out.println(threadId + " Starting"); 
...changeArray(); 

この共有配列を変更する予定です。 の書き込みを行った後で、(バリア上で)同期しようとします。あまりにも遅い!

ポイントは:あなたは10 異なる ThreadableClassインスタンスを持っています。それぞれ!で動作しています!​​キーワード...ここでは何の保護も提供していません!

が原因で:synchronizedがを2つ異なるスレッドがと同じオブジェクトで同じスレッドを呼び出すのを防ぎます。しかし、複数のオブジェクトを持ち、あなたのスレッドがそれらのメソッドを呼び出すとき異なるオブジェクトよりも、がありませんロック!言い換えれば

threadA to call changeArray() .. on itself 
threadB to call changeArray() .. on itself 
threadC to call changeArray() .. on itself 

...

:どのようなあなたのコードがないと、つまるところあなたはNのスレッドがその共有のアレイにアクセスできます。しかし、それらのnスレッドが同時にchangeArray()を入力することを許可します。

1つの簡単な修正。言い換えれば

private synchronized void changeArray() { 

を変更:同じモニタ上をロックn個のスレッドがに持っていることを確認してください。その場合は共有配列。

代わりに、changeArray()をそのThreadableClassのメソッドにする代わりに...

ArrayUpdater { 
    int arr[] to update 

    synchronized changeArray() ... 

は、そのクラスのインスタンスを作成するクラスを作成します。その同じインスタンスを各スレッドに与えます。これでsync'edメソッドは複数のスレッドが入ることを防ぎます!

+0

素晴らしい。どうもありがとうございます。私は今参照してください。あなたのソリューションを読んで、私はContollerクラスでThreadableClassの単一のインスタンスを作成し、同じインスタンスの各スレッドを渡すことにしました。 (ちょうど次の人に同じことを驚かせるための余分な文脈を提供しようとする)。再び、GhostCatに感謝します!あなたは本当に助けてくれました。 – SupposedlySleeping

+0

あなたはこれらの部品の周りで何をしているのか分かっているようですので、私はこれに何か様式的な改善がありますか?私はそれに従わなければならないすべての習慣は私ではない? (ハッピーバースデー!!!) – SupposedlySleeping

+0

ほんの少し。私はすぐに寝なければならないので、A)静的なメインを使っていても。そこにできるだけ少ないことをしようとする。同様に、その番号「n」を取り、スレッドのリストを作成するメソッドを作成します。それに引き続き取り組むもう1つの方法です。そしてあなたはメインだけで数を読み取り、それらのメソッドを呼び出します。 B)あなたのフィールドを**最終的なものにするようにしてください。そうすれば、コンパイラは値を割り当てられていることを確実にすることができます。 – GhostCat

0

あなたがnew ThreadableClass(barrier, arr)を使用して、各theardためnewインスタンスを提供しているので、基本的には、すべてtheadrsは異なるThreadableClassオブジェクトを使用しているので、以下に示すように、あなたが単一ThreadableClassオブジェクトを使用する必要があるので、あなたのコード同期の方法は、並列実行します。

ThreadableClass threadableClass= new ThreadableClass(barrier, arr); 
for (int i = 0; i < numThreads; i++) { 
    Thread newThread = new Thread(threadableClass); 
    threads.add(newThread); 
} 

重要な点は、同期はすべての時間でシングルスレッドのオブジェクトへのアクセス(すなわち、キー)を提供する程度です。スレッドごとに異なるオブジェクトを使用している場合、各スレッドに独自のキーがあるため(例のように)、スレッドはキーを待たない。

関連する問題