最近、コードベースでこの宝石を見つけました:
/** This class is used to "publish" changes to a non-volatile variable.
*
* Access to non-volatile and volatile variables cannot be reordered,
* so if you make changes to a non-volatile variable before calling publish,
* they are guaranteed to be visible to a thread which calls syncChanges
*
*/
private static class Publisher {
//This variable may not look like it's doing anything, but it really is.
//See the documentaion for this class.
private volatile AtomicInteger sync = new AtomicInteger(0);
void publish() {
sync.incrementAndGet();
}
/**
*
* @return the return value of this function has no meaning.
* You should not make *any* assumptions about it.
*/
int syncChanges() {
return sync.get();
}
}
これは次のように使用されます。
スレッド 1
float[][] matrix;
matrix[x][y] = n;
publisher.publish();
スレッド 2
publisher.syncChanges();
myVar = matrix[x][y];
スレッド 1 は、継続的に実行されるバックグラウンド更新スレッドです。スレッド 2 は HTTP ワーカー スレッドであり、読み取った内容が何らかの形で一貫性があるか原子的であるかを気にせず、書き込みが「最終的に」そこに到達し、並行性の神への供物として失われないことだけを気にします。
さて、これは私のすべての警告ベルをトリガーします。無関係なコードの奥深くに記述されたカスタム同時実行アルゴリズム。
残念ながら、コードの修正は簡単ではありません。同時プリミティブ行列に対する Java サポートは適切ではありません。これを修正する最も明確な方法は を使用するように見えますがReadWriteLock
、それはおそらくパフォーマンスに悪影響を及ぼすでしょう。正確さは明らかにより重要ですが、パフォーマンスに敏感な領域からそれを切り取る前に、これが正しくないことを証明する必要があるようです.
java.util.concurrent のドキュメントによると、次のようにhappens-before
関係が作成されます。
スレッド内の各アクションは、プログラムの順序で後で来るそのスレッド内のすべてのアクションの前に発生します。
揮発性フィールドへの書き込みは、同じフィールドの後続のすべての読み取りの前に発生します。volatile フィールドの書き込みと読み取りには、モニターの開始と終了と同様のメモリ整合性効果がありますが、相互排他ロックは必要ありません。
したがって、次のように聞こえます。
- マトリックス書き込みは公開前に発生します() (ルール 1)
- publish() は syncChanges() の前に発生 (ルール 2)
- syncChanges() マトリックス読み取りの前に発生 (ルール 1)
したがって、コードは実際にマトリックスの事前発生チェーンを確立しています。
しかし、私は確信していません。並行性は難しく、私はドメインの専門家ではありません。私は何を逃したのですか?これは本当に安全ですか?