对于 class 的多个实例,Volatile 关键字无法按预期工作

Volatile keyword does not work as expected with multiple instances of a class

我读过几乎所有的帖子,volatile(即使它不是静态的)变量在线程之间共享。当一个线程更新变量时,第二个线程将获得更新后的值。但是,当我 运行 在我的本地机器上使用下面的代码时, 运行ning java 7 。它没有给出预期的结果

代码-

public class StatciVolatile3 {

    public static void main(String args[]) {
        new ExampleThread2("Thread 1 ").start();
        new ExampleThread2("Thread 2 ").start();
    }

}

class ExampleThread2 extends Thread {
    private volatile int testValue = 1;

    public ExampleThread2(String str) {
        super(str);
    }

    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                System.out.println(getName() + " : " + i);
                if (getName().compareTo("Thread 1 ") == 0) {
                    testValue++;
                    System.out.println("Test Value T1: " + testValue);
                }
                if (getName().compareTo("Thread 2 ") == 0) {
                    System.out.println("Test Value T2: " + testValue);
                }
                Thread.sleep(1000);
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            }
        }
    }
}

结果是-

Thread 2  : 0
Test Value T2: 1
Thread 1  : 0
Test Value T1: 2
Thread 2  : 1
Test Value T2: 1
Thread 1  : 1
Test Value T1: 3
Thread 2  : 2
Test Value T2: 1
Thread 1  : 2
Test Value T1: 4

这里我们可以看到线程 T2 的测试值始终为 1。有人可以帮助我理解为什么会这样吗?

提前致谢!

我的预期结果是——两个线程都看到了更新值

Thread 1  : 0
Test Value T1: 2
Thread 2  : 0
Test Value T2: 2
Thread 1  : 1
Test Value T1: 3
Thread 2  : 1
Test Value T2: 3
Thread 1  : 2
Test Value T1: 4
Thread 2  : 2
Test Value T2: 4

testValue 是一个实例变量,因此 class 的每个实例都有一个单独的变量副本。您已经创建了两个实例:

   new ExampleThread("Thread 1 ").start();
   new ExampleThread("Thread 2 ").start();

... 并且每个都有自己的变量副本。线程 2 从不更新它的值,这就是它保持在 1 的原因。

如果你想让他们共享同一个变量,就把它设为static:

private static volatile int testValue = 1;

您的声明:

I have read in almost all the posts that volatile (even if it's not static) variable is shared among the the threads

... 表明对您所读内容的误读或误解。如果线程都访问同一个变量,则可以在线程之间看到对 volatile 变量的更改,但这并不意味着实例变量将在实例之间共享。

有评论指出'testValue++'不是原子操作。一般来说,确实应该避免对线程之间共享的易失性变量进行这种操作。但是,对于您的有限示例,这实际上不是问题,因为实际上只有一个线程会改变变量的值。

I have read in almost all the posts that volatile (even if it's not static) variable is shared among the the threads

这是一个不正确的陈述。 volatile 实例变量不在线程之间共享。 class 的 volatile 实例变量确保共享 class 相同 实例的所有线程将始终看到 class 的更新值=10=] class 的实例变量,而不是从缓存中读取值。

要解决问题中发布的特定代码示例的问题,您可以将 testValue 标记为 static

也就是说,volatile 不能保证原子性。操作 testValue++; 不是原子操作,因此如果多个线程可以执行 testValue++,则将 testValue 声明为 static 不会解决问题(这在您当前的示例中是不可能的).当多个线程可以执行 testValue++ 时,解决此问题的一种方法是将 testValue 声明为 AtomicInteger 并将其标记为 static

volatile关键字决定变量是否在本地线程缓存。

在您的示例中,您期望的结果并未出现,因为两个线程都在 testValue 父对象(在本例中为 ExampleThread2 对象)的独立实例上工作。如果 testValue 为 static,那么结果将如您所料。

在多CPU环境中,如果两个线程运行在不同的CPUs上访问同一个对象,它们将创建两个独立的本地缓存。因此,read/write 操作将是不明确的。在这种情况下 volatile 出现了。

当我们使用 volatile 时,缓存不会在本地创建,并且两个线程(运行 在不同的 CPUs 上)将在同一个更新副本上工作(当然适用时需要同步)。