[英]illustrating volatile : is this code thread-safe?
我想说明的使用和重要性volatile
用一个例子,如果这样做真的不给一个好的结果volatile
省略。
但我并不习惯使用volatile
。 如果省略volatile
,则以下代码的想法是导致无限循环,并且如果存在volatile
则完全是线程安全的。 以下代码是否是线程安全的? 你有其他任何使用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());
}
}
Victor是对的,您的代码存在问题:原子性和可见性。
这是我的版本:
private int count;
private volatile boolean stop;
private volatile boolean stopped;
@Override
public void run() {
while (!stop) {
count++; // the work
}
stopped = true;
System.out.println("Count 1 = " + count);
}
public void stopCounting() {
stop = true;
while(!stopped)
; //busy wait; ok in this example
}
public int getCount() {
if (!stopped) {
throw new IllegalStateException("not stopped yet.");
}
return count;
}
}
如果一个线程观察到stopped==true
,则保证工作完成并且结果可见。
从易失性写入到易失性读取(在同一变量上)之间存在一个先发生的关系,因此如果有两个线程
thread 1 thread 2
action A
|
volatile write
\
volatile read
|
action B
行动A发生在行动B之前; B中可以看到A中的写入
进一步简化@Elf示例,其他线程永远不会获得由其他线程更新的值。 删除System.out.println因为println中存在同步代码而out是静态的,以某种方式帮助其他线程获取flag变量的最新值。
public class VolatileExample implements Runnable {
public static boolean flag = true;
public void run() {
while (flag);
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new VolatileExample());
thread.start();
Thread.sleep(1000L);
flag = false;
thread.join();
}
}
我总是很难用一种令人信服的方式来说明并发性问题:好吧,很好, 以前发生的事情和事情都很好,但为什么要关心呢? 有真正的问题吗? 有很多很多写得不好,程序不合理的程序 - 而且它们大部分时间都在工作。
我曾经在“ 大部分时间工作 VS 作品 ”的修辞中找到一个度假胜地 - 但是,坦率地说,这是一种微弱的方法。 所以我需要的是一个可以明显区别的例子 - 最好是痛苦的。
所以这是一个实际上显示差异的版本:
public class VolatileExample implements Runnable {
public static boolean flag = true; // do not try this at home
public void run() {
long i = 0;
while (flag) {
if (i++ % 10000000000L == 0)
System.out.println("Waiting " + System.currentTimeMillis());
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new VolatileExample());
thread.start();
Thread.sleep(10000L);
flag = false;
long start = System.currentTimeMillis();
System.out.println("stopping " + start);
thread.join();
long end = System.currentTimeMillis();
System.out.println("stopped " + end);
System.out.println("Delay: " + ((end - start) / 1000L));
}
}
一个简单的运行显示:
Waiting 1319229217263
stopping 1319229227263
Waiting 1319229242728
stopped 1319229242728
Delay: 15
也就是说,正在运行的线程需要超过十秒 (此处为15)才能注意到有任何更改。
有了volatile
,你有:
Waiting 1319229288280
stopping 1319229298281
stopped 1319229298281
Delay: 0
也就是说,几乎立即退出。 currentTimeMillis
的分辨率大约为10ms,因此差异超过1000次。
请注意,这是Apple的(ex-)Sun JDK版本,带有-server
选项。 添加了10秒等待,以便让JIT编译器发现循环足够热,并对其进行优化。
希望有所帮助。
错误的代码,如果y已经是2,我们也不能假设x = 1:
Class Reordering {
int x = 0, y = 0;
public void writer() {
x = 1;
y = 2;
}
public void reader() {
int r1 = y;
int r2 = x;
}
}
使用volatile关键字的示例:
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
//uses x - guaranteed to see 42.
}
}
}
资料来源: http : //www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
更新我的回答是错误的,看到无可争议的回答。
它
不是
线程安全的,因为
访问
只有一个编写器线程。 如果有另一个编写器线程, count
不是
count
值将与更新的数量不一致。
通过检查getCount
方法中的stopped
volatile,可以确保count
数值对主线程的可见性。 这就是Concurrency in practice
书中的Concurrency in practice
称为piggybacking on synchronization
。
为了说明volatile
关键字在并发volatile
的重要性,您需要做的就是确保在单独的线程中修改和读取volatile
字段。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.