スレッドを「1つずつ」実行する

n個のスレッドがアクセスできるシングルトンクラスがあります。
各スレッドはこのクラスのインスタンスをロードし、ループ内でこのクラスのメソッドを呼び出します。

すべてのスレッドがメソッドを呼び出すことができるように、実行の流れを制御する必要があります。 スレッドはメソッドを任意の順序で呼び出すことができます。スレッドはループを移動する前に一度だけメソッドを実行する必要があります。

これは私がやろうとしたことです:

スレッド内:

 while ( some condition){
    ObjectType obj = theSingleton.getInstance().getSharedObject();
    obj.SomeMethod(threadID);
   if (obj.waitisneeded())
    synchronized (obj ) {
       obj.wait();
    }
}

SomeMethodで私がしたこと:

public  synchronized void SomeMethod(String threadID) {
 hashMap.put(threadID,true);
 some job here
}

とwaitisneededで:

public  synchronized boolean waitisneeded(){
{

   Iterator iter = hashMap.entrySet().iterator();

   boolean alldone = false;

   while (iter.hasNext()) { 
      Map.Entry me = (Map.Entry) iter.next();
      String key = me.getKey().toString();
      alldone = (Boolean)me.getValue();

      if(!alldone)  {
    return false;
       }
   }

  //set all values to false 
 iter = hashMap.entrySet().iterator();
 while (iter.hasNext()) { 
   Map.Entry me = (Map.Entry) iter.next();
    String key = me.getKey().toString();
   me.setValue(false);      
 }      
 this.notifyAll();
 return true;               

これを実行すると、予期せぬ結果とデッドロックが発生しました。

どうすれば修正できますか?

注:私はスレッドの作成方法を変更することはできません、私はそれらのメソッドを変更することができます! (さらに、待機中のスレッドのwhileループ)

2
「予想されるすべてのスレッドが現在シングルトンと呼ばれている」という点にいつ到達したのか、どのようにしてわかりますか?
追加された 著者 Thorbjørn Ravn Andersen,
それぞれが obj.SomeMethod()を呼び出す前に、いくつのスレッドが作成されているか知っていますか?
追加された 著者 The Scrum Meister,
@TheScrumMeisterスクラムマイスターはい、私はスレッドの数を知っている
追加された 著者 kenny,
@ThorbjørnRavn Andersen、コードにはgetSharedObjectにハッシュマップがあり、アクセスしたスレッドにフラグを立てます。すべてのスレッドがチェックされたら、私はスレッドを解放し、ハッシュマップをクリアして、すべてをやり直すことができます。
追加された 著者 kenny,

2 答え

スレッド数がわかっている場合は、 巡回バリア

static final CyclicBarrier barrier = new CyclicBarrier(numberOfThreads);

次に、すべてのスレッドが他のスレッドを待つようにしたいときは、次のように呼び出します。

barrier.await();

まだその行に到着していないスレッドがある場合、 await()を呼び出すスレッドはブロックされます。すべてのスレッドがそこに到着し、 await()を呼び出すと、それらはすべて再開します。

同期メカニズムを構築する必要があるたびに、パッケージ java.util.concurrent 専門家によって作成された素晴らしいクラスがたくさんあります。ほとんどの場合、何かカスタムは必要ありません。同じパッケージにいくつかのクラスがあり、待ち/通知を直接使う必要はほとんどありません。メソッドを直接;あなたが見ることができるように、あなたは非常に簡単に自分自身をデッドロックにすることができます!

2
追加された
まあ、私はあなたのアプリケーションがどのように動作するのか正確にはわかりません。私はあなたのシナリオに適応できると確信しています!全体的なアイデアは、レジュームする前にお互いに待たなければならないスレッドが、単一のサイクリックバリアを共有しなければならないということです。しかし、障壁を適切に公開してください! (つまり、CyclicBarriersが多数あるため、ConcurrentHashMapなどのインスタンスを保持するために並行コレクションを使用することをお勧めします)。
追加された 著者 Bruno Reis,
私は静的なので、CyclicBarrierに問題があると思う。私のアプリケーションでは、このシナリオは数回並行して起こります! Xスレッド(言及するのを忘れていました)では、getSharedObject isが生成され、シングルトンのhashmap変数に格納されます。すべてのスレッドのグループはそれぞれのオブジェクトにアクセスします。これは私がロックを使用しようとした理由、そしてgetSharedObject自体をロックとして使用することです!私は静的を使用する場合、私はすべてのgetSharedObjectのための新しい変数を作成する必要があります
追加された 著者 kenny,

これは、Rendez-Vous( "予約"を意味するフランス語)と呼ばれる典型的なマルチスレッド状況です。基本的には、あなたの N スレッドには予定があり、すべてが到達するまですべて待つべきです。 @Brunoが述べたように、 CyclicBarrier オブジェクトを使用して状況を管理できます。シナリオが1回だけ実行される場合は、 CountDownLatch を使用することもできます。

これを実現する方法は次のとおりです。

static final CountDownLatch rendezVousPoint = new CountDownLacth(numberOfThreads);

//Every thread does the following right before waiting on the rendez vous point
rendezVousPoint.countDown();
rendezVousPoint.await();

この状況では、最後のスレッドが到着してすべてのスレッドを解放するまで( rendezVousPoint count = 0になるまで)、すべてのスレッドは await()

0
追加された
私は CountDownLatch のこの(ab)使用が気に入らない。 CountDownLatch を覆してしまい、 CyclicBarrier (まさにこのシナリオを処理するために作られたもの)のように動作します。呼び出されたメソッド CDL.countDown() CDL.await()は、ほとんどが CB.await()のように動作しますが、通常、ワーカースレッドは CDL.countDown()を呼び出し、マスタースレッドのみが CDLを呼び出します。 await()を使用して、すべてのワーカースレッドがタスクを終了するまでブロックします。
追加された 著者 Bruno Reis,
コメントをいただきありがとうございます、私はあなたにこれに同意しない傾向があります。公式のJavaドキュメントによると、 CDL.countDown() CDL.await()を順番に実行することには何も問題ありません。ドキュメントごとに CyclicBarrier というバージョンは、 CountDownLatch の再利用可能な(数回設定とリセットが可能です)
追加された 著者 GETah,