Javaの文字列と並行性

This maybe a related question: Java assignment issues - Is this atomic?

私は、変更可能な文字列参照に作用するOPと同じクラスを持っています。しかし、めったにセットが起こらない。 (基本的にこの文字列は、強制的にリロードされるサーバー構成の一部です)。

public class Test {
 private String s;

 public void setS(String str){
  s = str;
 }

 public String getS(){
  return s;
 }
}

複数のスレッドがこの値を読み取るためにこの変数を叩くことになります。それを揮発性と宣言してパフォーマンス低下を招くことなく、安全にする最良の方法は何ですか?

私は現在、ReadWriteLockの方向に向かっていますが、私が理解する限り、ReadWriteのロックはスレッドのキャッシュから安全になりませんか?シンクロナイゼーションが起こらない限り?つまり、 volatile キーワードを使用するだけでよいのですか?

私の理解は正しいのですか?他のスレッドにメインメモリ内の変数の更新について手動で通知して、満月に一度だけローカルキャッシュを更新できるものはありますか?

volatile on this seems overkill given that the server application is designed to run for months without restart. By that time, it would've served a few million reads. I'm thinking I might as well just set the String as static final and not allow it mutate without a complete application and JVM restart.

4
私はIngoとここにいる:それを速くする前にそれを正しいものにする。揮発性の読み込みは、あなたのアプリのパフォーマンスを著しく低下させるものではありません。
追加された 著者 JB Nizet,
私は最初にあなたのアプリケーションのための揮発性のソリューションのオーバーヘッドを測定するでしょう。それが無視できる場合、質問はミュートであるかもしれません。
追加された 著者 Ingo Kegel,
私はあなたが "必要がなければ最適化しない"と言っていると確信しています。しかし、それは私の質問に正しく答えません。とにかくあなたのコメントをありがとう。
追加された 著者 user986139,
おそらく私はこれが科学的な(あなたが好きなら)関心事であり、必ずしもアプリケーションドメイン目的ではないと言うメモを追加すべきでしょうか?
追加された 著者 user986139,

4 答え

参照への読み書きは不可分です。発生する可能性がある問題は、読み取りと書き込み(更新)を実行しようとしているか、または書き込み後にすべてのスレッドが次の読み取り時にこの変更を参照することを保証することです。しかし、あなたの要求が何であるかはあなただけが言うことができます。

volatileを使用する場合は、キャッシュコヒーレントコピーを読み書きする必要があります。これは、ソケット間でもキャッシュが通信するため、メインメモリとの間でコピーを行う必要はありません。パフォーマンスには影響がありますが、キャッシュが使用されているわけではありません。

アクセスがメインメモリに至っても、1秒間に何百万回もアクセスできます。

2
追加された
@RonaldChan、私は volatile の使用に関するいくつかのノートを追加しました。
追加された 著者 Peter Lawrey,
基本的には、新しい参照が主メモリに設定されると、揮発性の使用による主メモリからの取り出しのみのスレッドのオーバーヘッドなしに、ローカルキャッシュを無効にする必要があります。しかし、私の理解や仮定が正しいかどうかは分かりませんでした。
追加された 著者 user986139,

なぜ可変文字列ですか?なぜ単純な静的Stringを持つConfigクラスではありませんか? configが更新されると、この静的参照が変更されます。これはアトミックな操作であり、スレッドの読み取りには問題ありません。同期がなく、ロックのペナルティはありません。

0
追加された
@solendil:あなたの仮定は間違っています。キャッシングは思ったよりも長くなる可能性があり、参照は揮発性または同期した方法でアクセスする必要があります。
追加された 著者 JB Nizet,
申し訳ありませんが、「スレッドキャッシュ」についてはわかりません。私はいくつかの非常に短命のキャッシングが時には単純な型(JITレジスタ、JNIなどで変わる)で起こることがあることを知っていますが、マルチスレッドの基本原理は同じメモリを共有する多くのプロセスですしたがってキャッシュはありません)。あなたの場合、私は、この設定文字列上のキャッシュは非常に短命であり、問​​題ではないはずだと推測します。
追加された 著者 solendil,
Configクラスへの静的な参照がスレッドキャッシュを処理する必要があるかどうか尋ねてもいいですか?もしそうなら、私はまだConfig noの静的リファレンスにvolatileキーワードを宣言しなければなりません。すべての取得が最新のconfigクラスオブジェクトへの最新の参照を取得できるようにするには?
追加された 著者 user986139,
残念なことに、並行性についての多くの記事や、ここではStackOverflowに関する矛盾する議論から、私は受け取っています。実行中のスレッドのローカルキャッシュが、これまでにメインメモリから最新の参照を読み取ることが保証されていないこと。特定の同期化ステップが発生しない限り。
追加された 著者 user986139,

このサーバーにクライアントに通知するには、オブザーバーのパターンを使用できます。サーバーの更新情報を取得することに興味がある人は、イベントに登録でき、サーバーは通知を配信します。これはリロードが頻繁ではないと述べたので、ボトルネックにならないはずです。

今度はこのスレッドを安全にするために、別のスレッドがサーバー状態の更新を処理し、状態が「更新中」であれば状態を確認して、完了するまで待機します。更新スレッドが完了すると、状態が「更新中」であれば状態をスリープ状態から外し、スリープ状態に移行するか、要求の処理を開始すると、状態を「更新中」から「更新済み」に変更する必要があります。

このアプローチでは、コード内に余分なifが追加されますが、アプリケーションを強制的に再起動せずにキャッシュを再ロードすることができます。

また、サーバの更新が頻繁ではないので、これはボトルネックにはなりません。

これが意味をなさないことを願っています。

0
追加された

volatile キーワードを避けるためには、 Test クラスに memory barrier "メソッドを追加することができます。例

public synchronized void sync() {
}

これにより、スレッドはメインメモリからフィールド値を再読み込みさせます。

また、セッターを変更する必要があります

 public synchronized void setS(String str){
    s = str;
 }

synchronized キーワードは、設定スレッドがメインメモリに直接書き込むようにします。

同期とメモリの障壁の詳細については、こちらを参照してください。

0
追加された
AFAIK、別のスレッドのローカルキャッシュに何かをプッシュすることはできません。スレッドはメモリバリアーに遭遇する必要があります。
追加された 著者 Ingo Kegel,
同期メソッドを各スレッドが定期的にポーリングする必要があると仮定するのは当然ですか?私はローカルキャッシュに変更を「プッシュ」する方法を望んでいました。
追加された 著者 user986139,
私はすぐに伝播するように変更する必要がなければ実行可能な代替案であるため、とにかく+1してください。私はJavaのロードブロッキングを打つかもしれない。
追加された 著者 user986139,