2012-06-26 17 views
6

言ってやるが、私は、データオブジェクトを持っています。また、スレッドプール、共有データ、Javaの同期

Collection<ValueRef> masterList = ...;

私は:各データオブジェクトは、マスター・コレクションに格納されて

class ValueRef { double value; }

各ジョブにデータオブジェクトのローカルコレクションがある(各データオブジェクトはmasterListにも表示されます)

class Job implements Runnable { 
    Collection<ValueRef> neededValues = ...; 
    void run() { 
     double sum = 0; 
     for (ValueRef x: neededValues) sum += x; 
     System.out.println(sum); 
    } 
} 

ユースケース:

  1. for (ValueRef x: masterList) { x.value = Math.random(); }

  2. 一部のジョブとジョブキューを移入します。

  3. ウェイクアップ各ジョブが

注意して評価されるまで、スレッドプール

  • 待ち:職務評価の間に、すべての値はすべて一定です。しかし、スレッドは、過去にジョブを評価し、キャッシュされた値を保持する可能性があります。

    質問:各スレッドが最新の値を確認するために必要な同期の最小量はいくらですか?

    私は、モニター/ロックパースペクティブから同期を理解していますが、キャッシュ/フラッシュの観点から同期しているとは分かりません(つまり、同期ブロックの入力/終了時のメモリモデルによって保証されているもの)。

    私には、メインメモリに新しい値をコミットするために値を更新するスレッドと、新しい値が読み込まれるようにキャッシュをフラッシュするワーカースレッドごとに一度同期する必要があるように感じます。しかし、私はこれをどうやって行うのが最善かわかりません。

    私のアプローチ:グローバルモニタを作成します。static Object guard = new Object();次に、マスターリストを更新しながらguardで同期します。最後に、スレッドプールを開始する前に、プール内のスレッドごとに1度、空のブロックにguardを同期させます。

    実際には、そのスレッドが読み取った値が完全にフラッシュされますか?または、同期ブロック内の値だけをタッチしますか?この場合、空のブロックではなく、ループ内で各値を一度読み取る必要がありますか?

    お時間をいただきありがとうございます。


    編集:私は、同期ブロックを終了すると、最初のすべての読み取り(その時点の後)はメインメモリに行くのですか?私が同期したものに関係なく?

  • +0

    揮発性キーワード – ControlAltDel

    +0

    を利用するためのほぼ完璧な場所のようですが、私は一度書く(事実上一定)が、潜在的に何百万回も読んでいます。揮発性は決してローカルにキャッシュされません。毎回スレッドプールを作成した場合、コードは(以前のキャッシュが存在しないため)細かいw/o同期/ volatileで動作します。 –

    +0

    私はここに揮発性の必要性が表示されません。 ValueRefが効果的に不変の場合、実際には不変にしてください。ダブルを使用します。スケジュールされる前にジョブごとに新しいコレクションを作成し、unmodifiableCollectionでラップします(リマインダーと同様)。あなたは何の問題を考えますか? –

    答えて

    3

    スレッドプールのスレッドが過去にいくつかのジョブを評価していることは問題ではありません。Executor

    Javadocは言う:

    メモリー整合性効果:スレッド内のアクション前キュータにRunnableオブジェクトを提出する起こる-前にその実行は、おそらく別のスレッドでは、開始されます。

    したがって、標準のスレッドプールの実装を使用し、ジョブを送信する前にデータを変更する限り、メモリの可視性の影響を心配する必要はありません。

    +0

    これは、ワーカースレッドでは、新しいジョブを待っている同期ブロックがあるためですか?そのブロックが終了すると、スレッド全体のキャッシュはクリアされますか?私は何かランダムに同期して同じ効果を得ることができますか? –

    +0

    @AndrewRaffensperger:どのように実装されているかは関係ありません。保証があり、それを提供する必要があります。最後の質問について - 基本的にはそうだが、意味をなさない。追加の同期手段がなければ、メインスレッドの同期ブロックの後に実行されるワーカースレッドのブロックを同期させることはできない。追加の同期手段があればそれは冗長です。 – axtavt

    2

    あなたは十分な音を計画しています。それはあなたが "スレッドプールを起こす"ことをどのように計画するかによって異なります。

    Javaメモリモデルでは、​​ブロックを入力する前にスレッドによって実行されたすべての書き込みが、そのロックで後で同期するスレッドに認識されます。

    マスターリストを更新している間にwait()コール(ブロック内にある​​にある)でワーカースレッドがブロックされていることが確認された場合、そのスレッドが起動して実行可能になると、マスタースレッドがこれらのスレッドに表示されます。

    しかし、java.util.concurrentパッケージに高レベルの並行性ユーティリティを適用することをお勧めします。これらはあなた自身のソリューションよりも堅牢であり、深く掘り下げる前に並行性を学ぶのに適しています。ただ、明確にする


    :それはチェックは、作業者が実施するタスクを持っているかどうかを確認するために行われる同期ブロックを使用せずに、ワーカースレッドを制御することはほぼ不可能です。したがって、コントローラスレッドがジョブに行った変更は、ワーカースレッドが起動する前に発生します。​​ブロック、または少なくともvolatile変数がメモリバリアとして動作する必要があります。しかし、私はどのようにこれらのいずれかを使用してスレッドプールを作成すると思うことはできません。

    java.util.concurrencyパッケージを使用する利点の一例として、この考えてみましょう。あなたはそれにwait()呼び出しで​​ブロックを使用することができ、またはvolatile変数をビジー待機ループ。スレッド間のコンテキスト切り替えのオーバーヘッドのために、ビジー状態の待機は実際には特定の条件の下でより良好に実行できます—一見一見する恐ろしい考えは必要ありません。

    同時実行性ユーティリティ(この場合はおそらくExecutorService)を使用する場合は、環境、タスクの性質、および他のスレッドの必要性を考慮して、最適な選択を行うことができます所定の時間に最適化のレベルを自分で達成することは、不必要な作業の多くです。

    +0

    私はjava.util.concurrentのオーバーヘッドを余裕がありません。私の例のデータは一度更新され、マルチスレッド評価中に "一定"になります。私はそのデータが既存の他のスレッドにどのように見えるようになっているか興味があります。どの同期化ブロックも、同期化された先行する関係を使用しなくても、この可視性を引き起こすように見える。あるいは、何らかの明示的な同期を必要とせず、 "すべての値の変更が行われるまでジョブが実行されない"ということは、請求書に適合します。 –

    +1

    @AndrewRaffensperger Right。それが必要な場合は、正確さのために必要な最小のオーバーヘッドを持つ 'java.util.concurrent'ユーティリティがあります。同時実行性ユーティリティのオーバーヘッドが高いと仮定するのは間違いです。実際には、compare-and-swapのような高性能並行性ツールへのアクセスを提供します。これをJavaで実装することは、 'AtomicXXX'クラスの背後にある最適化されたネイティブコードよりも遅くなるでしょう。ほとんどの他のユーティリティでは、同様のパフォーマンスの利点があります。 – erickson

    1

    Collection<ValueRef>ValueRefは、コレクションへの参照を公開した後で、コレクションの値を変更できない、または少なくとも変更しないのはなぜですか。それでは、同期に関する心配はありません。

    これは、コレクションの値を変更し、新しいコレクションを作成して新しい値を入れたいときです。値が設定されると、コレクション参照新しいジョブオブジェクトを渡します。

    コレクションのサイズが大きすぎてメモリにほとんど収まらず、2つのコピーを持つことができない場合、またはコレクションをスワップすると、ガベージコレクタ(スレッドコードに変更可能なデータ構造を使用する前に、これらのいずれかが問題であることを証明してください)。

    +0

    私はいつもValueRefを再構築したり、スレッドプールを再構築したりすることができ、私の問題は消えます。しかし、私の実際の実装では、データ構造が非常に複雑で、コードが頻繁に呼び出され、各評価がスレッドプールを再構築するとオーバーヘッドが大きくなります。 –

    関連する問題