volatile 关键字好像没什么用?
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class Main implements Runnable {
private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
private volatile int bar = 0;
private AtomicInteger count = new AtomicInteger(0);
private static final int NUM_THREADS = 25;
public static void main(String[] args) {
Main main = new Main();
for(int i = 0; i < NUM_THREADS; i++)
new Thread(main).start();
}
public void run() {
int i = count.incrementAndGet();
cdl1.countDown();
try {
cdl1.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
bar = i;
if(bar != i)
System.out.println("Bar not equal to i");
else
System.out.println("Bar equal to i");
}
}
每个Thread
进入run方法并通过从AtomicInteger
获取值来获取唯一的、线程限制的int
变量i
code> 称为 count
。然后,每个线程
等待名为cdl1
的CountDownLatch
(当最后一个线程
到达锁存器时,所有线程
已发布)。当闩锁被释放时,每个线程都会尝试将其受限的 i
值分配给名为 bar
易失性、int
>。
我希望除了一个线程之外的每个线程都会打印出“Bar not equal to i”,但每个线程都会打印“Bar equal to i”。呃,wtf 如果不是这样的话,volatile
真的能做到吗?
每个Thread
都尝试在同一时间设置bar
的值是有意为之。
编辑:
根据答案,将代码更改为:
...
bar = i;
try {
Thread.sleep(0);
} catch(InterruptedException e) {
e.printStackTrace();
}
...
确保在变量的设置和读取之间浪费一点时间。
现在,Bar 的相同/不同值的打印结果为 50/50。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class Main implements Runnable {
private final CountDownLatch cdl1 = new CountDownLatch(NUM_THREADS);
private volatile int bar = 0;
private AtomicInteger count = new AtomicInteger(0);
private static final int NUM_THREADS = 25;
public static void main(String[] args) {
Main main = new Main();
for(int i = 0; i < NUM_THREADS; i++)
new Thread(main).start();
}
public void run() {
int i = count.incrementAndGet();
cdl1.countDown();
try {
cdl1.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
bar = i;
if(bar != i)
System.out.println("Bar not equal to i");
else
System.out.println("Bar equal to i");
}
}
Each Thread
enters the run method and acquires a unique, thread confined, int
variable i
by getting a value from the AtomicInteger
called count
. Each Thread
then awaits the CountDownLatch
called cdl1
(when the last Thread
reaches the latch, all Threads
are released). When the latch is released each thread attempts to assign their confined i
value to the shared, volatile
, int
called bar
.
I would expect every Thread
except one to print out "Bar not equal to i", but every Thread
prints "Bar equal to i". Eh, wtf does volatile
actually do if not this?
It is a deliberate intention that each Thread
attempts to set the value of bar
at exactly the same time.
EDIT:
In light of the answer, changed code to this:
...
bar = i;
try {
Thread.sleep(0);
} catch(InterruptedException e) {
e.printStackTrace();
}
...
To ensure that a little time is wasted between the set and read of the variable.
Now the print is 50/50 on same/different value for Bar.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
它不是。你滥用它了。 Herb Sutter 在此处发表了一篇很棒的文章,对此进行了更详细的解释。
基本思想是
易失性
使变量变得不可优化。它并不使它们线程安全。It's not. You're misusing it. There is a great article here by Herb Sutter that explains it in more detail.
The basic idea is that
volatile
makes variables unoptimisable. It does not make them thread safe.要回答“Volatile 实际上有什么作用?”:
Volatile 与可见性有关。在Java的线程模型中,如果线程A写入常规共享字段,则不能保证线程B永远看到A写入的值,除非线程B以某种方式同步。易失性是同步机制之一。
与非易失性字段不同,当线程 A 写入易失性字段并且线程 B 随后读取它时,B 一定会看到新值而不是旧版本。
(实际上 volatile 的作用甚至更多 - 线程 B 不仅会看到该字段的新值,还会看到 A 在设置 volatile 变量之前写入的所有其他内容。它建立了发生之前的关系)。
To answer the 'WTF does volatile actually do?':
volatile is all about visibility. In Java's thread model, if a thread A writes into a regular shared field, there is no guarantee that a thread B will ever see the value written by A, unless the threads are synchronized somehow. volatile is one of the synchronization mechanisms.
Unlike non-volatile fields, when thread A writes into a volatile field and thread B later reads it, B is guaranteed to see the new value and not an older version.
(Actually volatile does even more - thread B will not only see the new value of the field, but everything else written by A before it set the volatile variable as well. It established a happened-before relationship).
您应该做的是将
volatile int
实例替换为AtomicInteger
。请参阅此处 。What you should do is replace your instance of
volatile int
withAtomicInteger
. See here.我认为你的意思是这样写:
它会像你预期的那样打印“Bar not equal to i”。
I think you meant to write this:
Which prints "Bar not equal to i" like you expected.
JVM 决定线程何时运行,而不是您。如果感觉就像握住刚刚释放锁存器的其中一个再保持 10 毫秒,只是因为它可以做到这一点。闩锁释放后,它们仍然需要等待轮到执行。除非您在 25 核计算机上运行它,否则它们不会在机器内部接近“同一时间”的任何地方分配栏。由于您所做的只是几个原始操作,因此在释放下一个操作之前,其中一个操作不可能在其时间片内完成!
The JVM decides when the threads run, not you. If it felt like holding one of the ones whose latch just released for another 10ms, just because, it can do that. After the latch releases, they still have to wait for their turn to execute. Unless you're running it on a 25 core computer, they're not all assigning bar at anywhere near 'the same time' down inside the machine. Since all you're doing is a couple of primitive operations, it's extremely unlikely that one of them won't finish inside its time slice before the next one gets released!