什么时候线程从主存储器读取非易失性变量而不是工作存储器?

时间:2018-04-16 07:51:43

标签: java jvm visibility

public class Main {

public static void main(String[] args) throws InterruptedException {
    Worker w = new Worker();
    w.start();

    sleepQuietly(1000);

    w.alive = false;
    w.join();
}

static class Worker extends Thread {
    boolean alive = true;

    @Override
    public void run() {
        while (alive) {
        }
    }
}

static void sleepQuietly(long millis) {
    try {
        Thread.sleep(millis);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

如果我运行上面的main方法,主线程将不会停止。但是如果我运行下面的main方法,main方法将停止。

public class Main {

public static void main(String[] args) throws InterruptedException {
    Worker w = new Worker();
    w.start();

    sleepQuietly(1000);

    w.alive = false;
    w.join();
}

static class Worker extends Thread {
    boolean alive = true;

    @Override
    public void run() {
        while (alive) {
            sleepQuietly(1); 
        }
    }
}

static void sleepQuietly(long millis) {
    try {
        Thread.sleep(millis);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

“内存模型保证,在最终发生上述操作的情况下,对一个线程所做的特定字段的特定更新最终会被另一个线程看到。但最终可能是一个任意长的时间。长时间的代码在不使用同步的线程中,与其他线程相关的字段值无可救药。特别是,编写循环等待其他线程写入的值总是错误的,除非字段是易失性的或通过同步“< / p>

我在http://gee.cs.oswego.edu/dl/cpj/jmm.html上面阅读过,这是我的问题: 1.在第一个示例中,为什么工作线程无法看到主线程更新的值,因为JMM保证一个线程对特定字段的特定更新最终会对另一个线程可见

  1. 为什么Thread.sleep()有所不同?

2 个答案:

答案 0 :(得分:4)

  

JMM保证对特定字段的特定更新   一个线程最终将对另一个

可见

它仅在某些条件下保证(最终)可见性。您所指的链接明确说明了这一点。写入非易失性字段不属于这种情况。

  

为什么Thread.sleep()有所不同?

Thread.sleep()是一种未被JIT内联的本机方法。调用此方法会中断loop invariant hosting优化,因此每次迭代都会重新读取对象字段。但是,此行为是特定JVM的实现细节。无法保证程序在其他版本的JVM上的行为方式相同。此外,JLS明确warnsThread.sleepThread.yield都没有任何同步语义。

答案 1 :(得分:1)

看看:https://help.semmle.com/wiki/display/JAVA/Spin+on+field

class Spin {
    public boolean done = false;

    public void spin() {
        while(!done){
        }
    }
}


class Spin { // optimized
    public boolean done = false;

    public void spin() {
        boolean cond = done;
        while(!cond){
        }
    }
}
  

该方法重复while循环,直到完成字段的值由另一个线程设置。但是,编译器可以优化代码,如第二个代码片段所示,因为完成的字段没有标记为volatile,并且循环体中没有可以更改done值的语句。优化版本的自旋循环,即使另一个线程设置为true。