別の質問から、Javaの揮発性バージョンカウンターを使用してseqlockを効率的に実装できるかどうか疑問に思いました。
これが典型的な実装です。この場合、ライタースレッドは1つだけになります。
class Seqlock {
private volatile long version = 0;
private final byte[] data = new byte[10];
void write(byte[] newData) {
version++; // 1
System.arraycopy(newData, 0, data, 0, data.length); // 2
version++; // 3
}
byte[] read() {
long v1, v2;
byte[] ret = new byte[data.length];
do {
v1 = version; // 4
System.arraycopy(data, 0, ret, 0, data.length); // 5
v2 = version; // 6
} while (v1 != v2 || (v1 & 1) == 1);
}
}
基本的な考え方は、書き込みの前後にバージョン番号をインクリメントし、読者がバージョン番号が同じで偶数であることを確認することで「一貫した」読み取りが行われたことを確認することです。奇数は「書き込み中」を示します。
バージョンは揮発性であるため、ライタースレッドとリーダースレッドの主要なアクションの間には、あらゆる種類の発生前の関係があります。
ただし、(2)での書き込みが(1)の上に移動して、リーダーに書き込みの進行状況が表示されるのを妨げる原因がわかりません。
たとえば、各行の横にあるコメントのラベルを使用した、次の揮発性の読み取りと書き込みの同期順序(揮発性data
ではないため、同期順序の一部ではない読み取りと書き込みも表示され、インデントされています)。
1a (version is now 1)
2a (not part of the synchronization order)
3 (version is now 2)
4 (read version == 2, happens before 3)
5 (not part of the synchronization order)
6 (read version == 2, happens before 4 and hence 3)
1b (second write, version is now 3)
2b (not part of the synchronization order)
5(データの読み取り)と2b(データの2回目の書き込み)の間に発生しないというISTM。したがって、読み取りの前に2bが発生し、誤ったデータが読み取られる可能性があります。
それが本当なら、助けとして宣言write()
しsynchronized
ますか?