public class Volatile {
volatile int x = 0;
public static void main(String a[]) {
Volatile y = new Volatile();
test t1 = new test(y);
test t2 = new test(y);
t1.setName("A");
t2.setName("B");
t1.start();
t2.start();
}
}
class test extends Thread {
Volatile v;
test(Volatile v) {
this.v = v;
}
@Override
public void run() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName() + "Says Before " + v.x);
v.x++;
System.out.println(Thread.currentThread().getName() + "Says After " + v.x);
}
}
}
输出
ASays Before 0
BSays Before 0
BSays After 2
BSays Before 2
BSays After 3
ASays After 1 <--- Is it a cache value ?
BSays Before 3
ASays Before 3
BSays After 4
BSays Before 5
BSays After 6
ASays After 5 <--- Is it a cache value ?
ASays Before 6
ASays After 7
ASays Before 7
ASays After 8
无处不在,我找到了关于volatile 的共同点
保证不会被缓存以及不同的线程 将看到更新后的值
但是,从上面的例子来看,线程有不同的值(旧/缓存值),还是因为实现不当?
答案 0 :(得分:2)
标记变量volatile将阻止JVM缓存该值,但不会为您处理同步问题(例如在修改变量和打印变量之间交换线程)。
例如,线程A输出before 0
然后被换出,以便线程B运行。该值仍为零,因为A尚未更新它。 B然后更新它,然后A返回并更新它,然后打印它。这意味着你最终会得到类似的东西:
ASays Before 0
BSays Before 0
ASays After 2
这不太理想。
此外,println
本身不是原子的,因此它可以在流中断,导致线程持有一个过时的打印值(即,稍后在输出流中显示的那个)理想情况下。)
要正确更新和打印,您应该在需要以原子方式使用变量的块周围使用synchronized
,例如:
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " before " + v.x);
v.x++;
System.out.println(Thread.currentThread().getName() + " after " + v.x);
}
在您的特定情况下,您不能使用this
,因为它是线程对象,这意味着它们有两个,因此它们不会在您需要时相互阻塞。 / p>
你可以通过一些kludge解决这个问题,在线程类中引入一个静态对象:
static Object o = new Object();
并将其用于同步:
synchronized (o) {
System.out.println(Thread.currentThread().getName() + " before " + v.x);
v.x++;
System.out.println(Thread.currentThread().getName() + " after " + v.x);
}