返回信息流大家都知道,java的volatile关键字实现了jmm中storeload的读写屏障,保证了从堆copy到栈的过程中变量是最新鲜的,本质是一个最轻量级的读写所,但是不能解决原子操作问题。
以上是教课书上的内容,今天突然心血来潮,想试一下,带volatile的关键字能够一直保证线程修改可见性么?比如thread1某时刻读入了volatile int a=0,thread2一秒后a++,thread1此时看的是几呢?如果a没有volatile修饰,thread1看到的又是多少呢?
于是写下了如下代码:
{code}
public class Test {
public static void main(String[] args) {
final TT a = new TT(0);
Thread t1 = new Thread(new RR(a));
Thread t2 = new Thread(new RRR(a));
t1.start();
t2.start();
System.out.println("fi");
}
private static class TT {
private int a = 0;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public TT(int a) {
this.a = a;
}
public String toString() {
return "" + a;
}
}
private static class RR implements Runnable {
private TT t;
public RR(TT t) {
this.t = t;
}
@Override
public void run() {
System.out.println("i am t1, now a is" + t);
try {
TimeUnit.SECONDS.sleep(2L);
System.out.println("i am t1, now a is" + t);
TimeUnit.SECONDS.sleep(3L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static class RRR implements Runnable {
private TT t;
public RRR(TT t) {
this.t = t;
}
@Override
public void run() {
System.out.println("i am t2, now a is" + t);
try {
TimeUnit.SECONDS.sleep(1L);
t.setA(1);
System.out.println("i am t2, now a is" + t);
TimeUnit.SECONDS.sleep(4L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
{code}
悲剧的楼主发现怎么写,带不带volatile,两个线程看到的都是最新的a=1,于是就不解了,求帮助啊!!!
这是一条镜像帖。来源:北邮人论坛 / java / #41855同步于 2015/6/21
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
[问题]请教java关键字volatile的问题
DivineDm
2015/6/21镜像同步16 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
1 知乎这个例子我本机跑是正常的,能够输出22222
2 底下人的解释也都是大众解释,道理大家都明白,我只是想知道我这种为什么不行,因为这段代码是我根据自己的理解写的,如果不对,说明我的理解有点问题[ema1]
3 追问一个小问题,volatile修饰的变量是每次读取都会从主内存中同步一份么?
【 在 lee8464 的大作中提到: 】
: 你这个例子不能证明volatile的用法,用这个例子可以,http://www.zhihu.com/question/27513916
【 在 DivineDm 的大作中提到: 】
: 1 知乎这个例子我本机跑是正常的,能够输出22222
: 2 底下人的解释也都是大众解释,道理大家都明白,我只是想知道我这种为什么不行,因为这段代码是我根据自己的理解写的,如果不对,说明我的理解有点问题
: 3 追问一个小问题,volatile修饰的变量是每次读取都会从主内存中同步一份么?
逐个解决:
1.楼主确定不加volatile能输出2222?这个例子也是一样的http://stackoverflow.com/questions/5816790/the-code-example-which-can-prove-volatile-declare-should-be-used
2.不定义为volatile不表示线程对某一变量的修改(暂时在CPU的寄存器中?)不被写回主存,只是时间问题,这个要多久写回,就涉及到编译器优化,不是代码级别能控制的,但是这个写回时间其实人是感觉不到的,也许就几毫秒甚至微秒的差别,不管你sleep怎么设计,都难以捕获这个延迟。所以楼主的代码不能证明,所以我就举了知乎上的那个例子,stackoverflow的例子跟知乎上的例子原理一样,但是更复杂。
3.对的。
总的来说尽量少用volatile,除非你要成为优化大师。
【 在 DivineDm 的大作中提到: 】
: 大家都知道,java的volatile关键字实现了jmm中storeload的读写屏障,保证了从堆copy到栈的过程中变量是最新鲜的,本质是一个最轻量级的读写所,但是不能解决原子操作问题。
: 以上是教课书上的内容,今天突然心血来潮,想试一下,带volatile的关键字能够一直保证线程修改可见性么?比如thread1某时刻读入了volatile int a=0,thread2一秒后a++,thread1此时看的是几呢?如果a没有volatile修饰,thread1看到的又是多少呢?
: 于是写下了如下代码:
: ...................
jsr-133明确保证:对volatile的写happens-before其后对它的读.
LZ可以看下面这个链接,加深对volatile的理解!!
https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
Volatile从来不保证所谓的“最新”,因为“最新”本身没办法定义。java只能依靠happen-before关系。你的程序是用定时器的,但定时器不能建立任何happen-before关系。所以,程序里对a的一读一写两者之间没有happen-before关系,所以那个读操作可能读到0也可能读到1。不管a是不是volatile
来自「北邮人论坛手机版」
【 在 DivineDm 的大作中提到: 】
: 1 知乎这个例子我本机跑是正常的,能够输出22222
: 2 底下人的解释也都是大众解释,道理大家都明白,我只是想知道我这种为什么不行,因为这段代码是我根据自己的理解写的,如果不对,说明我的理解有点问题
: 3 追问一个小问题,volatile修饰的变量是每次读取都会从主内存中同步一份么?
java 1.0-1.4的memory model有分主内存和局部内存,但是java 1.5以后就再也不用这种说法了,而是完全使用happen-before关系来讨论“读操作允许读到哪个写操作的数据”。只要允许读到多种不同的结果,那么程序执行的结果就不是确定性的。
【 在 AAAMWAAA 的大作中提到: 】
: jsr-133明确保证:对volatile的写happens-before其后对它的读.
这句话是对的,但是“其后”的意思是按照synchronization order这个顺序的“其后”,和定时器没有关系。如果有两个线程T1和T2,对共享的volatile变量a(初始值为0)进行操作:
T1: read a
同时,不加同步地,T2在做:
T2: write a = 1
如果T1读到a=0,那么可以反推这个读操作在synchronization order中位于T2的写操作之前;
如果T1读到a=1,那么可以反推这个读操作在synchronization order中位于T2的写操作之后。
而sleep对这个顺序没有任何决定性的影响。
: LZ可以看下面这个链接,加深对volatile的理解!!
: https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
1 我确定,你看stackoverflow第一个回答第一个回复就说明了某些情况是能够终止的,应该是跟jdk版本有关
2 这一点我想到了,但是写回主存感觉还是猜想没有证据的
3 哦,多谢
【 在 lee8464 的大作中提到: 】
: 逐个解决:
: 1.楼主确定不加volatile能输出2222?这个例子也是一样的http://stackoverflow.com/questions/5816790/the-code-example-which-can-prove-volatile-declare-should-be-used
: 2.不定义为volatile不表示线程对某一变量的修改(暂时在CPU的寄存器中?)不被写回主存,只是时间问题,这个要多久写回,就涉及到编译器优化,不是代码级别能控制的,但是这个写回时间其实人是感觉不到的,也许就几毫秒甚至微秒的差别,不管你sleep怎么设计,都难以捕获这个延迟。所以楼主的代码不能证明,所以我就举了知乎上的那个例子,stackoverflow的例子跟知乎上的例子原理一样,但是更复杂。
: ...................
恩,指令重排序的屏障作用,这确实是volatile的作用之一,可以防止发布逃逸
另外,补充一点,volatile还可以做到8字节赋值的原子性
【 在 AAAMWAAA 的大作中提到: 】
: jsr-133明确保证:对volatile的写happens-before其后对它的读.
: LZ可以看下面这个链接,加深对volatile的理解!!
: https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile