为什么线程一可以读到线程二的非volatile值?请大佬指教。

wJT9tlx
  • 2
新手上路,请多包涵
public static volatile int i = 0;
public static volatile int j = 0;
private static boolean aa = false;
/**
 * 可见性 * * @param args
 * @throws InterruptedException
 */
public static void main(String[] args) throws InterruptedException {

    //线程一
    new Thread(() -> {
 System.out.println("thread 1 run.....");
 while (!aa) {
 int b = i;
 } System.out.println("thread 1 end.....");
 }).start();
 
    TimeUnit.SECONDS.sleep(2);
    //线程二
    new Thread(() -> {
 aa = true;
 System.out.println("更新aa为true");
 try {
 TimeUnit.SECONDS.sleep(5);
 } catch (InterruptedException e) {
 } }).start();
}
//结果,线程二更新完aa变量,线程一跳出了循环。
回复
阅读 425
3 个回答

image.png

循环读取volatile 变量 对volatile修饰的变量每次都会从主内存读取
主要是因为 i aa 在同一个缓存行中 每次读取都会附带更新aa
所以 等线程二 更新aa 这个不会立即刷新 主要是后面的println()语句中加锁了 会强制刷新该线程

public void println(boolean x) {
    synchronized (this) { //这里会刷新当前线程工作空间 aa就写到主内存中 然后就读取跳出
        print(x);
 newLine();
 }
}

个人见解:这种情况觉得没必要这么执着,约定 只是加了volatitle一定能保证其他线程看见更新,并不代表不加就看不见,不是非黑即白,如果不加volatitle有可能看得见,也有可能看不见。至于什么时候看得见,可能是sleep,可能是sync关键字,可能是其它的volatitle变量赋值了,都可能借机刷一把工作内存。但都存在不确定性,jvm并不保证可见性。

wJT9tlx
  • 2
新手上路,请多包涵

线程二等待5s的时候,线程一就能读取到线程二更新的非volatile的值,想请教一下是哪个操作导致线程二的 aa = true; 刷新到主内存/造成缓存失效的?
补充@好难(说的缓存行相关知识:https://zhuanlan.zhihu.com/p/...

了解到缓存行,我改了下代码,将对象进行填充:
public class Test {

private static class Padding {
    public volatile long p1, p2, p3, p4, p5, p6, p7 = 0;
}
private static class Entity extends Padding {
    public volatile long x = 0L;
    public volatile long p1, p2, p3, p4, p5, p6, p7;
}
private static Entity entity = new Entity();
private static boolean aa = false;
/**
  • 可见性 @param args
  • @throws InterruptedException

*/
public static void main(String[] args) throws InterruptedException {

    new Thread(() -> {
        while (!aa) {
            long b = entity.x;
        }
        System.out.println("thread 1 end.....");
    }).start();
    TimeUnit.SECONDS.sleep(2);
    new Thread(() -> {
        aa = true;
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
        }
    }).start();
}

}
结果仍然是线程一会结束,各路大神一起看下?

你知道吗?

宣传栏