易失性布尔值

3
如果我有一个易失的布尔变量(我们称之为valid),下面这段Java代码是线程安全的吗?
if (valid)
  return;
valid = true;

或者,我需要同步吗?因为只有在valid为false时才设置为true(因此valid的设置取决于其当前值)?

1
Volatile并不能解决竞态条件。Volatile解决的是“valid”中存储的值被共享到所有其他具有访问权限的线程的问题。因此,如果“valid”不是静态的,并且没有指向包含“valid”的对象的静态引用,则没有其他线程会知道“valid”。 - Marco
7个回答

8

这需要同步,因为如果一个线程在赋值之前将valid评估为false,然后暂停执行,然后另一个线程在将valid设置为true之前也检查valid为false,则会有两个线程从此处运行代码(这可能不是您想要的)。


只有当实际上有两个线程执行相同的代码(或进行写操作)时,这才是真的。 - Michael Borgwardt
如果你的代码假设在同一时间只有一个线程可以运行它,那么它就不是“线程安全”的。如果你假设某个变量只能被代码中的某个小部分访问并写入,那么这种假设必须适用于整个模块,否则也会出现同样的问题! - KernelJ
更明确地说,如果所有线程只执行读操作,那么一切都很好;但是,如果其中一个线程具有写入值的能力,则需要在使用它的所有代码上进行同步,以避免出现一致性问题。 - KernelJ

5
使用AtomicBoolean。实例可以同时进行检查和设置。

是的,好建议... 但是它首先需要你意识到你需要同步 :-). - Tom

4

它不是线程安全的。但如果这是整个代码,那就无关紧要。


2
编辑: 一个全面优秀的替代方案是AtomicBoolean,它使用低级操作实现条件更新而无需同步。

该标志存在两个单独的(即非原子)访问,因此除非该线程是唯一执行标志写操作的线程,否则需要同步。即使如此,为了以防未来更改,最好也要进行同步。


1

你的代码不是线程安全的,但它是否安全取决于你的其他代码。

你需要确保在单个线程中只执行一次跟随valid = true的代码吗?如果是这样,那么你的代码就不安全了,因为任意数量的线程都可以读取validfalse值,然后将其设置为true。例如:

if (valid)
  return;
// Imagine every single one of your threads stops and blocks here.
// They will all wake up again and set valid to true and then
// execute the code to follow.
valid = true;

但是,如果你只想保证在任何线程中执行valid = true之后的代码最多只会被执行一次...那么你现在的做法就可以。但如果这是你需要的行为,我会通过其他方式来实现,因为在这种情况下使用volatile看起来就像你不知道自己在做什么。例如,你可以不跨线程共享valid变量,让每个线程只执行一次代码。

此外,在考虑同步和volatile时如果有疑问...就使用同步吧。通常这样更清晰,而且将获得从使用volatile获得的所有东西,除了它可以更容易地理解代码如何工作。


1

线程安全是一个系统范围的属性。你不能孤立地看待一段代码并称其为线程安全/不安全。它取决于其他线程如何与它交互以及一致性要求是什么。话虽如此,大多数线程安全的设计都有while()循环而不是if()块,因此,你的设计很可能是不正确的:)


1
使用原子操作或同步操作来保证线程安全,而不是使用while循环。您说得对,线程安全是系统属性,但如果valid是共享变量,则此代码本质上不适合多线程。 - extraneon
我并不是在说使用while循环可以使其线程安全。我是说线程安全的代码通常有while循环,而if块通常会指出错误。 - irreputable

1
根据Brian Goetz的一篇很棒的论文https://www.ibm.com/developerworks/java/library/j-jtp06197/,"只有在受到限制的情况下,才能使用volatile变量代替锁。为了实现所需的线程安全性,必须满足以下两个条件:"
  • 写入变量不依赖于其当前值。
  • 该变量不参与与其他变量的不变式。
基本上,这些条件说明可以写入volatile变量的有效值集是独立于任何其他程序状态的,包括变量的当前状态。因此,它必须与锁同步。如果不这样做,它就不是线程安全的。

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