揮発性を使用
これは、あるスレッドが別のスレッドを気にする場合ですか? 次に、JMM FAQに答えがあります。
ほとんどの場合、一方のスレッドは他方が何をしているか気にしません。しかし、そうするとき、それが同期の目的です。
OP のコードは現状のままで安全であると言う人に対しては、次のことを考慮してください。 Java のメモリ モデルには、新しいスレッドの開始時にこのフィールドがメイン メモリにフラッシュされることを保証するものは何もありません。さらに、変更がスレッド内で検出されない限り、JVM は操作を自由に並べ替えることができます。
理論的に言えば、リーダー スレッドが有効なプログラム コードへの「書き込み」を確認できる保証はありません。実際には、最終的にはそうなりますが、いつになるかはわかりません。
validProgramCodes メンバーを「volatile」として宣言することをお勧めします。速度の違いはごくわずかであり、JVM の最適化がどのように導入されても、現在および将来のコードの安全性が保証されます。
具体的な推奨事項は次のとおりです。
import java.util.Collections;
class Metadata {
private volatile Map validProgramCodes = Collections.emptyMap();
public Map getValidProgramCodes() {
return validProgramCodes;
}
public void setValidProgramCodes(Map h) {
if (h == null)
throw new NullPointerException("validProgramCodes == null");
validProgramCodes = Collections.unmodifiableMap(new HashMap(h));
}
}
不変性
でラップするだけでなくunmodifiableMap
、マップ ( ) をコピーしていますnew HashMap(h)
。これにより、setter の呼び出し元がマップ "h" を更新し続けても変更されないスナップショットが作成されます。たとえば、マップをクリアして新しいエントリを追加する場合があります。
インターフェイスに依存
スタイル上の注意として、 and のような具象型ではなく、 and のような抽象型で API を宣言する方がよい場合がよくありますList
。これにより、将来、具象型を変更する必要がある場合に柔軟性が得られます (ここで行ったように)。Map
ArrayList
HashMap.
キャッシング
「h」を「validProgramCodes」に割り当てた結果は、単にプロセッサのキャッシュへの書き込みである可能性があります。新しいスレッドが開始された場合でも、共有メモリにフラッシュされていない限り、「h」は新しいスレッドに表示されません。適切なランタイムは、必要でない限りフラッシュを回避します。使用volatile
は、フラッシュが必要であることを示す 1 つの方法です。
並べ替え
次のコードを想定します。
HashMap codes = new HashMap();
codes.putAll(source);
meta.setValidProgramCodes(codes);
setValidCodes
が単に OP の場合validProgramCodes = h;
、コンパイラは次のようにコードを自由に並べ替えることができます。
1: meta.validProgramCodes = codes = new HashMap();
2: codes.putAll(source);
ライター行 1 の実行後、リーダー スレッドが次のコードの実行を開始するとします。
1: Map codes = meta.getValidProgramCodes();
2: Iterator i = codes.entrySet().iterator();
3: while (i.hasNext()) {
4: Map.Entry e = (Map.Entry) i.next();
5: // Do something with e.
6: }
ここで、ライター スレッドがリーダーの行 2 と行 3 の間のマップで "putAll" を呼び出すとします。イテレーターの基になるマップで同時変更が発生し、実行時例外がスローされます。テスト中に生成されます。
並行プログラミング
別のスレッドが何をしているかを気にする 1 つのスレッドがあるときはいつでも、あるスレッドのアクションが他のスレッドから見えるようにするために、ある種のメモリ バリアが必要です。あるスレッドのイベントが別のスレッドのイベントの前に発生する必要がある場合は、明示的に示す必要があります。それ以外の保証はありません。実際には、これはvolatile
またはを意味しsynchronized
ます。
ケチらないでください。正しくないプログラムがどれだけ速く仕事を失敗しても、問題ではありません。ここに示されている例は単純で不自然ですが、予測不可能でプラットフォームの影響を受けやすいため、特定して解決するのが非常に難しい現実世界の同時実行バグを示しているので安心してください。
その他のリソース