对于 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 上)将在同一个更新副本上工作(当然适用时需要同步)。
我读过几乎所有的帖子,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 上)将在同一个更新副本上工作(当然适用时需要同步)。