4

另一个问题让我想知道seqlock 是否可以用 Java 中的 volatile 版本计数器有效地实现。

这是一个原型实现,对于这种情况,只有一个编写器线程:

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),从而导致读者看到正在进行的写入。

例如,以下 volatile 读取和写入的同步顺序,使用每行旁边注释中的标签(还显示了data非 volatile 的读取和写入,因此不属于同步顺序的一部分,缩进):

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)

ISTM认为在5(数据的读取)和2b(数据的第二次写入)之间没有happens-before,因此2b可能发生在读取和读取错误数据之前。

如果这是真的,那么声明write()synchronized帮助吗?

4

1 回答 1

2

在 java 中,您可以非常简单地实现共享缓冲区(或其他对象):

public class SharedBuffer {

  private volatile byte[] _buf;

  public void write(byte[] buf) {
    _buf = buf;
  }

  public byte[] read() {
    // maybe copy here if you are worried about passing out the internal reference
    return _buf;
  }
}

显然,这不是“seqlock”。

于 2013-02-12T13:10:25.070 回答