这个 BitSet 示例如何打印 (false, true) 或 (true, false)?

huangapple go评论62阅读模式
英文:

How can this BitSet example print (false,true) or (true, false)?

问题

我试图通过遵循Aleksey Shipilëv的博客文章来理解JMM。

上面的示例让我感到困惑。

解释:有3个线程,前两个线程(第1列和第2列)在没有任何锁定的情况下运行set(),而第3个线程(第3列)等待前两个线程终止,然后尝试读取前两个线程写入的值。

如果两个线程分别运行bs.set(1)bs.set(2)而没有任何锁定,它们如何可能产生不一致的结果?

根据内部结构,BitSet是使用long[]和位移实现的。

位移操作是一个常数时间操作,对吗?我根本无法理解这是如何可能的。

英文:

I am trying to understand the JMM by following the blog post by Aleksey Shipilëv

这个 BitSet 示例如何打印 (false, true) 或 (true, false)?

The above example is breaking my mind.

> Explanation: There are 3 threads the first 2 threads (the 1st and 2nd column) run set() without any locking and the 3rd thread (3rd column) waits for the previous threads to terminate and then tries to read the values written by the previous threads.

If 2 threads run bs.set(1) and bs.set(2) respectively without any locking how can they give inconsistent results?

According to the internals the BitSet is implemented using long[] and bitshifts.

Bit shifting is a constant time operation, right? I simply cannot work out how this is even possible.

答案1

得分: 1

假设位被存储在一个长整型变量 v 中。

  • 当只设置第1位时,v 等于2。
  • 当只设置第2位时,v 等于4。
  • 当两个位都被设置时,v 等于6(即 2|4)。

如果按顺序设置它们,会得到如下结果:

  • v 起始值为0。
  • bs.set(1)
    • 读取 v(0)
    • 加上2以表示第1位开启
    • 更新 v(2)
  • bs.set(2)
    • 读取 v(2)
    • 加上4以表示第2位开启
    • 更新 v(6)

现在 v 等于6,所以两个位都是开启的。


如果同时设置它们,可以得到如下结果:

  • v 起始值为0
  • 线程1:bs.set(1)
  • 线程2:bs.set(2)
  • 线程1:读取 v(0)
  • 线程2:读取 v(0)
  • 线程1:将 v 设置为2(即 0|2
  • 线程2:将 v 设置为4(即 0|4

现在 v 设置为4,所以第2位是开启的,而第1位是关闭的。

英文:

Suppose bits are stored inside a long variable v.

  • When just bit 1 is set, v would equal 2.
  • When just bit 2 is set, v would equal 4.
  • When both bits are set, v would equal 6 (i.e. 2|4).

If you set them sequentially, you get something like this:

  • v starts at 0.
  • bs.set(1)
    • Read v (0)
    • Add 2 to indicate that bit 1 is on
    • Update v (2)
  • bs.set(2)
    • Read v (2)
    • Add 4 to indicate that bit 2 is on
    • Update v (6)

Now v is 6, so both bits are on.


If you set them simultaneously, you can get something like this:

  • v starts at 0
  • Thread 1: bs.set(1)
  • Thread 2: bs.set(2)
  • Thread 1: Read v (0)
  • Thread 2: Read v (0)
  • Thread 1: Set v to 2 (i.e. 0|2)
  • Thread 2: Set v to 4 (i.e. 0|4)

Now v is set to 4, so bit 2 is on and bit 1 is off.

答案2

得分: 0

根据作者的陈述:BitSet Javadocs 表示多线程使用应该同步,因此这可以说是一个人工示例。

JVM 不保证 Bitwise OR: ior, lor 的操作是原子的。

参见:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.11.3

BitSet 也不尝试进行原子更改。

参见:https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/BitSet.java

/**
 * 将指定索引处的位设置为 {@code true}。
 *
 * @param  bitIndex 位索引
 * @throws IndexOutOfBoundsException 如果指定的索引为负
 * @since  1.0
 */
public void set(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);

    words[wordIndex] |= (1L << bitIndex); // 恢复不变性

    checkInvariants();
}

在多处理器系统中无法确定 lor 何时被执行。

更准确地说:

`lload`  words[wordIndex]
`lor`    (1L << bitIndex)
`lstore` words[wordIndex]

这3个命令可以在两个线程之间随时切换。

英文:

As the Author stated: BitSet Javadocs say multi-threaded usages should be synchronized, so this is arguably an artificial example

The JVM makes no guarantees of atomic operations for Bitwise OR: ior, lor.

See: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.11.3

The BitSet make not attempt to make atomic changes either.

See: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/BitSet.java

/**
 * Sets the bit at the specified index to {@code true}.
 *
 * @param  bitIndex a bit index
 * @throws IndexOutOfBoundsException if the specified index is negative
 * @since  1.0
 */
public void set(int bitIndex) {
    if (bitIndex &lt; 0)
        throw new IndexOutOfBoundsException(&quot;bitIndex &lt; 0: &quot; + bitIndex);

    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);

    words[wordIndex] |= (1L &lt;&lt; bitIndex); // Restores invariants

    checkInvariants();
}

When the lor is carried can not be determined in a multiprocessor system.

To be more precise:

`lload`  words[wordIndex]
`lor`    (1L &lt;&lt; bitIndex)
`lstore` words[wordIndex]

These 3 commands can switch at anytime between the 2 threads.

huangapple
  • 本文由 发表于 2023年3月7日 17:39:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75660218.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定