说明 volatile :这段代码是线程安全的吗?
我试图用一个例子来说明 易失性 的使用和重要性,如果省略 易失性 ,实际上不会给出好的结果。
但我不太习惯使用易失性
。以下代码的想法是,如果省略 易失性
,则导致无限循环,如果存在 易失性
,则完全线程安全。下面的代码是线程安全的吗?您是否有任何其他使用 易失性
的实际且简短的代码示例,如果没有它,会给出明显不正确的结果?
这是代码:
public class VolatileTest implements Runnable {
private int count;
private volatile boolean stopped;
@Override
public void run() {
while (!stopped) {
count++;
}
System.out.println("Count 1 = " + count);
}
public void stopCounting() {
stopped = true;
}
public int getCount() {
if (!stopped) {
throw new IllegalStateException("not stopped yet.");
}
return count;
}
public static void main(String[] args) throws InterruptedException {
VolatileTest vt = new VolatileTest();
Thread t = new Thread(vt);
t.start();
Thread.sleep(1000L);
vt.stopCounting();
System.out.println("Count 2 = " + vt.getCount());
}
}
I'm trying to illustrate the use and importance of volatile
with an example that would really not give a good result if volatile
was omitted.
But I'm not really used to using volatile
. The idea of the following code is to cause an infinite loop if volatile
is omitted, and be perfectly thread-safe if volatile
is present. Is the following code thread-safe? Do you have any other realistic and short example of code that uses volatile
and would give an obviously incorrect result without it?
Here's the code:
public class VolatileTest implements Runnable {
private int count;
private volatile boolean stopped;
@Override
public void run() {
while (!stopped) {
count++;
}
System.out.println("Count 1 = " + count);
}
public void stopCounting() {
stopped = true;
}
public int getCount() {
if (!stopped) {
throw new IllegalStateException("not stopped yet.");
}
return count;
}
public static void main(String[] args) throws InterruptedException {
VolatileTest vt = new VolatileTest();
Thread t = new Thread(vt);
t.start();
Thread.sleep(1000L);
vt.stopCounting();
System.out.println("Count 2 = " + vt.getCount());
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
维克多是对的,您的代码存在问题:原子性和可见性。
这是我的版本:
如果一个线程观察到
stopped==true
,则可以保证工作完成并且结果可见。从易失性写入到易失性读取(在同一个变量上)存在发生之前关系,因此如果有两个线程,
操作 A 发生在操作 B 之前; A 中的写入对 B 可见。
Victor is right, there are issues with your code: atomicity and visibility.
Here's my edition:
If a thread observes that
stopped==true
, it's guaranteed that the work completes and the result is visible.There is a happens-before relation from volatile write to volatile read (on the same variable), so if there are two threads
action A happens-before action B; writes in A are visible by B.
对我来说,以令人信服的方式说明并发问题总是很困难:好吧,发生在之类的事情都很好,但为什么要关心呢?真的有问题吗?有很多很多写得很糟糕、同步很差的程序——但它们在大多数时间仍然有效。
我曾经在“大部分时间都有效 VS 有效”的言辞中寻找解决方案,但坦率地说,这是一种薄弱的方法。所以我需要的是一个能让差异变得明显的例子——而且最好是痛苦的。
所以这里有一个版本确实显示了差异:
一个简单的运行显示:
也就是说,运行线程需要超过十秒(此处为 15 秒)才能注意到任何< /em> 改变。
使用
易失性
,您可以:也就是说,(几乎)立即退出。
currentTimeMillis
的分辨率约为 10ms,因此差异超过 1000 倍。请注意,它是 Apple 版本的(前)Sun JDK,带有
-server
选项。添加 10 秒等待是为了让 JIT 编译器发现循环足够热,并对其进行优化。希望有帮助。
It was always hard to me to illustrate concurrency problems in a convincing way: well, fine, it's all nice about happens-before and stuff, but why should one care? Is there a real problem? There are lots and lots of poorly written, poorly synchronized programs—and they still work most of the time.
I used to find a resort in a "works most of the time VS works" rhetoric—but, frankly, it's a weak approach. So what I needed is an example which would make difference obvious—and, preferably, painful.
So here is a version which actually does show the difference:
A simple run shows:
That is, it takes more than ten seconds (15 here) for a running thread to notice there was any change.
With
volatile
, you have:that is, exiting (almost) immediately. The resolution of
currentTimeMillis
is around 10ms, so the difference is more that 1000 times.Note it was Apple's version of (ex-)Sun JDK, with
-server
option. The 10-second wait was added in order to let JIT compiler find out that the loop is hot enough, and optimize it.Hope that helps.
进一步简化 @Elf 示例,其中其他线程永远不会获得其他线程更新的值。删除 System.out.println 因为 println 中有同步代码,而 out 是静态的,这可以帮助其他线程获取标志变量的最新值。
Simplifying @Elf example further, where the other thread will never get the value which was updated by other thread. Removing System.out.println as there is synchronized code inside println and out is static, somehow that helps the other thread to get the latest value of flag variable.
为了说明 易失性 关键字在并发性方面的重要性,您所需要做的就是确保在单独的线程中修改和读取易失性字段。
To illustrate the importance of the
volatile
keyword when it comes to concurrency, all you need to do is make sure that thevolatile
field is modified and read in a separate threads.更新 我的答案是错误的,请参阅来自reputable的答案。
它
不是线程安全的,因为无法访问,只有一个编写器线程。如果存在另一个写入线程,count
count
的值将与更新次数不一致。通过检查
getCount
方法内的stopped
易失性来确保count
值对主线程的可见性。这就是并发实践
书中所谓的搭载同步
。UPDATE My answer is wrong, see answer from irreputable.
It's
notthread-safe, sinceaccess tothere's only one writer thread. Should there be another writer thread, value ofcount
is notcount
would be become inconsistent to the number of updates.Visibility of
count
value to main thread is ensured by checkingstopped
volatile insidegetCount
method. This is what is calledpiggybacking on synchronization
inConcurrency in practice
book.错误的代码,如果 y 已经是 2,我们也不能假设 x = 1:
使用 volatile 关键字的示例:
来源:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
Wrong code with which we cannot assume x = 1 also if y is already 2:
Example of use of volatile keyword:
Source: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html