以下是volatile
关键字的使用示例。
public class Test3{
public static volatile boolean stop = false;// if volatile is not set, the loop will not stop
public static void main(String[] args) throws InterruptedException{
Thread thread = new Thread(){
public void run() {
int i=0;
while(!stop){
i++;
// add this line
// System.out.println(i);
// or this block
// try {
// Thread.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
};
thread.start();
Thread.sleep(2000);
stop = true;
}
}
很容易理解,如果设置volatile
,JVM应该在检查其值时从内存加载更新的值,然后while循环可以按预期停止。但问题是,静态变量不应该同时改变吗?可能会有一些延迟,但最终应该检测到这种变化。没有?我已经测试过,如果我们添加一些打印代码或睡眠代码,可以检测到这种变化吗?有人可以教我为什么喜欢这样吗?也许是关于JMM。
答案 0 :(得分:3)
从时钟时间的角度来看,时间对于内存可见性没有意义。重要的是同步操作之间的同步顺序。读取和写入非volatile
字段不是同步操作,因此在没有任何其他同步操作的情况下,它们之间没有排序。
所以即使主线程在一年前完成,所以写入必须从主线程的角度来看,子线程可以继续运行,永远运行;写作没有从它的角度发生。它也不知道主线程已经终止。请注意,执行能够检测到其他线程已终止的操作是可以建立订单的同步操作。
但是由于实际的程序行为也依赖于JIT编译器和优化器,因此某些代码更改可能会产生影响,即使它不能得到保证。
E.g。插入sleep
并不意味着任何内存可见性:
值得注意的是,
Thread.sleep
和Thread.yield
都没有任何同步语义。特别是,在调用Thread.sleep
或Thread.yield
之前,编译器不必将寄存器中缓存的写入刷新到共享内存,编译器调用{之后也不必重新加载缓存在寄存器中的值{1}}或Thread.sleep
。
但它可能会阻止优化器将循环视为需要优化的热点。
当您插入Thread.yield
语句时,System.out.println
的内部同步可能会影响整体内存可见性,但这种效果也无法保证,因为主线程不会同步在PrintStream
上。
顺便说一下,甚至没有保证在相同优先级的线程之间发生抢先线程切换。因此,如果JVM在将CPU返回主线程之前尝试在调用PrintStream
之后完成子线程,那将是一个有效的执行。
在该执行方案中,循环中没有start()
,子线程永远不会放弃CPU,因此sleep
永远不会设置为stop
,即使声明为{{ 1}}。这将是避免轮询循环的另一个原因,尽管可能没有抢先的线程切换的真实生活执行环境。今天的大多数执行环境甚至都有多个CPU,所以不放弃CPU不会阻止其他线程执行。
但是,为了正式更正,您应该在写入true
变量和读取变量之间执行排序,例如声明变量volatile
并插入可能导致变量的操作当stop
仍为volatile
时,线程最终会释放CPU。
答案 1 :(得分:2)
很容易理解,如果设置了volatile,JVM应该加载 在检查其值时从内存更新值,然后是while 循环可以按预期停止。
volatile
是某种rule
或mechanism
,而不是上面的具体实施。它用于在线程之间建立happends-before
关系:
使用volatile变量可降低内存一致性的风险 错误,因为对volatile变量的任何写入都会建立一个 在与之后的相关读取之前发生 变量。 这意味着始终对volatile变量进行更改 其他线程可见。更重要的是,这也意味着当a 线程读取一个volatile变量,它不仅看到最新的变化 对于挥发性,还有导致代码的副作用 变化强>
如果没有volatile
或其他同步,静态变量的更新可能会被其他线程看到延迟,并且无法永远看到,因为{ {3}}。 不确定。即使你添加一些打印代码或睡眠代码,并发现它有效,但这并不意味着它仍然可以在其他环境或其他时刻工作。
但是,如果您在while loop
和main
帖子中添加打印代码:
while(!stop){
i++;
System.out.println(i);
}
和
stop = true;
System.out.println("some");
JMM可以保证在循环中检测到stop = true
(至少在oracle jdk 8u161上),这是因为System.out.println
是memory barriar,也可以构建synchronized
1}}涉及的线程之间的关系,请参阅源代码:
happens-before