访问/写入布尔对象是否需要同步?

6

这可能看起来是一个非常愚蠢的问题。 考虑以下:

我有一个简单的布尔对象,它有一个getter和一个setter。现在,这两个方法都经常从很多线程中调用。

  1. 我需要为此布尔值同步吗?
  2. 布尔赋值是原子操作吗?

[更新]: 我已经知道了Atomic Boolean。我已经有了很多不同的解决方案,但我特别想要得到上面2个问题的答案和理由。


你是否要创建一个独立于java.lang.Boolean类的Boolean类? - Matthew T. Staebler
7个回答

10

不,布尔访问在机器代码级别上并不是原子性的,尽管在Java中只需要"1个操作"。

因此,是的,您需要对布尔进行同步。

请参见此演示文稿的4-6幻灯片以获取代码示例。

另外,您不应该在布尔上进行同步


1
x=true; 是原子操作吗?x 是布尔类型。 - Suraj Chandran
@Suraj:你不应该在布尔值上进行同步(1),也不应该在非final对象上进行同步(2),这两个指南并不重叠。即使你的布尔变量是final的,你仍然不应该将其用作同步对象。 - Matthew T. Staebler
@Aeth....为什么?如果我的布尔值是final的,那么将其用作锁定对象就没有问题了,对吗? - Suraj Chandran
2
@Suraj:不,你仍然不应该将它用作锁。请记住,锁是基于对象而不是对象的引用。根据你如何获取它,对布尔值的引用可能指向与其他引用共享的对象。例如,Boolean.TRUE(Boolean) trueBoolean.valueOf(true)很可能都引用同一个对象,这是完全有效的。 - Matthew T. Staebler
1
@DVK:Boolean对象是不可变的。Boolean对象的所有方法都可以保证是原子性的。 - Matthew T. Staebler
显示剩余2条评论

3
  1. 是的。但如果它是由一个线程编写的标志,并且您想确保所有线程都能看到它,那么一种廉价的替代方法是使用volatile

  2. 是的 - 即使对象的内部表示(即Boolean包装器中实际的boolean标志)为64位,并且因此可能在并发情况下被“拆分”,布尔值只能具有两个值,对吧?因此,普通的赋值(获取设置)是原子的,但如果您正在执行其他任何操作(例如检查-然后-操作),例如x = !x,则除非同步,否则它当然不是原子的。


如果标志被写入多个线程,为什么volatile不是有效的解决方案? - Matthew T. Staebler
@Aeth:在某些情况下,这可能是一个有效的解决方案,但它缺乏AtomicBoolean的compareAndSet操作/使用同步实现的类似功能。如果write的值取决于变量的当前值,则volatile不适用于多线程写入。但当然它也有其用途。 - Joonas Pulakka

3
  1. 从技术角度来看,在一个线程中进行的写入操作无需同步才能在另一个线程中被感知。您需要的是一个happens-before边缘。很可能会使用volatile或synchronized来实现happens-before边缘。这两种技术都会产生synchronized-with边缘。因此,在实践中,您可能会使用同步来管理布尔变量的状态。
  2. 是的。请注意,您并没有改变Boolean对象的状态,只是修改了对Boolean对象的引用。语言规范的第17.7节规定,“对引用的写入和读取始终是原子性的。”

更新:让我详细解释一下需要happens-before边缘的原因。如果没有happens-before边缘,则一个线程对变量所做的更改不能保证被其他线程感知。问题不仅在于变化可能在坏时机(例如在读取和写入之间)被察觉。变化也可能永远不会被感知。

假设我们有一个布尔变量,将其初始化为false。然后我们启动两个线程。第一个线程将变量设置为true并停止。第二个线程不断检查变量,直到它变为真,然后停止。不能保证第二个线程将永远看到变量为true。


2

谢谢,我已经知道了Atomic Boolean。我已经有很多不同的解决方案,但我特别想要上述两个问题的答案和理由。 - Suraj Chandran
这取决于对象的内部表示,是32位还是64位?我认为后者肯定会有问题。不确定32位系统是否安全,为了保险起见,最好同步它们。如果只涉及一个对象,则AtomicXXXX比synchronized更快。 - Chuk Lee

1
  1. 不需要,但是要声明变量为volatile,这样所有访问该布尔值的线程都能反映出其值。如果你看一下AtomicBooleanset(..)方法,它也没有同步。

  2. 是的,实际上赋值是原子性的。只是简单地设置值并不需要同步。然而,如果你想做像这样的事情:

    if (!bool) {
       bool = false;
    }
    

    那么你就需要同步(或者使用比同步更高效的AtomicBoolean)。


1

0

即使它是原子的,由于您可能会在某个时候检查该值,因此仍然存在同步问题。 例如: if (boolVar==true) -> 其他线程接管 do_something();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接