用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都必须遵守这一点。