5

状態がいくつかのフィールドで構成される共有オブジェクトへのスレッド間のアクセスを同期する必要があります。言う:

class Shared{
String a; Integer b;
//constructor, getters and setters
....
}

私はおそらく多くのスレッドがこのオブジェクトを読んでいます。

//readers
shared.getA();
shared.getB();

特定の時点で書き込みを行うスレッドは 1 つだけです。

//writer
shared.setA("state");
shared.setB(1);

今私の質問は、読み取りスレッドが一貫性​​のない状態で共有オブジェクトを見つけないようにする方法です。

スレッド間の一貫性がvolatile解決策であるという多くの回答を読みましたが、それが複数のフィールドでどのように機能するかはわかりません。例えば、それで十分ですか?

volatile  String a; volatile Integer b;

別の解決策は、共有オブジェクトを不変にし、AtomicReference を使用することです。

AtomicReference<Shared> shared = ....

そして、ライターは参照を交換するだけです:

Shared prev = shared.get(); 
Shared newValue = new Shared("state",1);
while (!shared.compareAndSet(prev, newValue)) 

そのアプローチは正しいですか?ありがとう!

更新私の設定では、共有オブジェクトはから取得されるConcurrentHashMap<Id,Shared>ため、コメントは、不変のアプローチを使用するか、共有の更新をすべて一緒に同期することであることに同意します。ただし、完全を期すために、上記の を使用した解決策ConcurrentHashMap<Id,AtomicReference<Shared>>が実行可能か、間違っているか、または単に不必要であるかを知っておくとよいでしょう。誰でも説明できますか?ありがとう!

4

4 に答える 4

1

一貫性を保つために A と B の両方を一緒に記述する必要がある場合 (名前と社会保障番号など)、1 つのアプローチは、すべての場所で使用し、単一の結合されたセッターsynchronizedを記述することです。

public synchronized void setNameAndSSN(String name, int ssn) {
   // do validation checking etc...
   this.name = name;
   this.ssn = ssn;
}

public synchronized String getName() { return this.name; }

public synchronized int getSSN() { return this.ssn; }

そうしないと、リーダーは新しい名前のオブジェクトを「見る」ことができますが、SSN は古いものになります。

不変のアプローチも理にかなっています。

于 2014-01-22T16:08:09.337 に答える
1

@Mikhailが彼の答えで言っているように、Shared不変にしてオブジェクト全体を置き換えることは良いアプローチです。何らかの理由でそのアプローチを使用したくない、または「使用できない」場合は、すべてのフィールドSharedが同じロックで保護されていること、およびそれらが一緒にのみ変更されることを確認できます (update私の例を参照) 、矛盾した状態で見ることは不可能です。

例えば

class Shared {
  private String a;
  private String b;
  public synchronized String getA() {
    return a;
  }
  public synchronized String getB() {
    return b;
  }
  public synchronized void update(String a, String b) {
    this.a = a;
    this.b = b;
  } 
}
于 2014-01-22T15:28:42.417 に答える
1

フィールドを volatile とマークしたり、メソッドを同期化しても、アトミック性は保証されません。

ライターは原子性に注意する必要があります。

ライターは、同期ブロック内のすべてのセッター (アトミックに更新する必要があります) を呼び出す必要があります。同期 (共有) { shared.setA() shared.setB() ... }

これが機能するには、共有オブジェクト内のすべてのゲッターも同期する必要があります。

于 2014-01-22T16:31:40.750 に答える