用java解释本例中的volatile

用java解释本例中的volatile,java,Java,我有如下代码: class Example { private volatile int testValue = 0; public int getTestValue() { return testValue; } public void setTestValue(int testValue) { this.testValue = testValue; } public void increment() {

我有如下代码:

class Example {
    private volatile int testValue = 0;

    public int getTestValue() {
       return testValue;
    }

    public void setTestValue(int testValue) {
       this.testValue = testValue;
    }

    public void increment() {
       this.testValue += 1;
    }
}

class PrintThread extends Thread {
    private Example example;    
    private int x = 0;

    public PrintThread(Example example) {   
       this.example = example;
       x = example.getTestValue();  
    }

    public void run() {
        while(true) {
            if(x != example.getTestValue()) {   // block 1
                System.out.println("printThread: " + example.getTestValue());
                x = example.getTestValue();
            }
        }
    }
}

class IncrementorThread extends Thread {

    private Example example;

    public IncrementorThread(Example example) {
        this.example = example;
    }

    public void run() {

        while(true) {
          example.increment();
          System.out.println("incrementorThread: " + example.getTestValue());

           try {
              Thread.sleep(800);
           } catch(Exception ex) {

           }
        }
    }
}

public class VolatileExample {
    public static void main(String args[]) {
        Example ex = new Example();
        new IncrementorThread(ex).start();
        new PrintThread(ex).start();
    }
}

当我删除示例类中的volatile关键字时,我永远看不到PrintThread的输出。在PrintThread中,当我打印出example的testValue时,example对象的值仍然更新,但“block 1”中的代码永远不会执行。两个线程仍然访问同一个对象,有人能给我解释更多细节吗?关于在这种情况下受影响的volatile关键字

您应该使用原子整数而不是volatile字段。要了解这一点的重要性,请尝试运行下面的代码。这里有3种类型的变量,普通
int
volatile int
AtomicInteger
。只有
AtomicInteger
才能确保值的线程安全。运行这个简单的代码之后,您将看到原因

public class Test {

    private int threadCount = 10;
    private int nonVolatileCount = 0;
    private volatile int volatileCount = 0;
    private AtomicInteger atomicCount = new AtomicInteger(0);
    private CountDownLatch startLatch = new CountDownLatch(threadCount);
    private CountDownLatch endLatch = new CountDownLatch(threadCount);

    private class Task implements Runnable {

        public void run() {
            startLatch.countDown();
            try {
                startLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 1000000; i++) {
                nonVolatileCount++;
                volatileCount++;
                atomicCount.incrementAndGet();
            }
            endLatch.countDown();
        };
    }

    public static void main(String[] args) throws InterruptedException {
        new Test().go();

    }

    public void go() throws InterruptedException {
        for (int i = 0; i < threadCount; i++) {
            new Thread(new Task()).start();
        }
        endLatch.await();
        System.out.println("non volatile counter: " + nonVolatileCount);
        System.out.println("    volatile counter: " + volatileCount);
        System.out.println("      atomic counter: " + atomicCount.get());
    }

}
公共类测试{
私有int线程数=10;
私有int非易失性计数=0;
私有volatile int volatileCount=0;
私有AtomicInteger atomicCount=新的AtomicInteger(0);
private CountDownLatch startatch=新的CountDownLatch(线程计数);
私有CountDownLatch endLatch=新的CountDownLatch(线程计数);
私有类任务实现可运行{
公开募捐{
倒计时();
试一试{
惊吓,等待;
}捕捉(中断异常e){
e、 printStackTrace();
}
对于(int i=0;i<1000000;i++){
非易失性计数++;
volatileCount++;
atomicCount.incrementAndGet();
}
endlack.countDown();
};
}
公共静态void main(字符串[]args)引发InterruptedException{
新测试().go();
}
public void go()引发InterruptedException{
对于(int i=0;i
如果不使用
volatile
,JVM可以免费使用线程本地存储来存储
testValue
;因此,您永远不会看到递增的结果。这就是volatile的作用:它迫使变量在每次访问时被重新读取。我必须指出,我不同意“你永远看不到递增的结果”的说法。Ofc认为会有增量,但例如在循环中有1M次迭代,1M次增量操作(多线程)的结果不会是1M,而是更少。多少钱?没人知道。我说的是非易失性变量,但即使使用易失性变量,这样的操作也不会准确。@antoniosss问题是,内存模型没有指定
testValue
非易失性事件中的行为;当然,可能会有JVM实现,每次都会重新读取,但这不是必需的。然而,
volatile
的语义是强制的:对
volatile
变量的写入总是在读取该变量之前“发生”。这意味着最终读取线程将看到增量的结果。任何JVM都必须遵守这一点。