我只是在线程视角问...可能已经多次回答了,但请帮助我理解这一点。
参考这里的帖子 Volatile Vs Static in java
要求一个静态变量值也将是所有线程的一个值,那么我们为什么要选择volatile呢?我找到了以下示例:
public class VolatileExample {
public static void main(String args[]) {
new ExampleThread("Thread 1 ").start();
new ExampleThread("Thread 2 ").start();
}
}
class ExampleThread extends Thread {
private static volatile int testValue = 1;
public ExampleThread(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 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中删除静态,则获得结果:
Thread 1 : 0
Test Value T1: 2
Thread 2 : 0
Test Value T2: 1
Thread 1 : 1
Test Value T1: 3
Thread 2 : 1
Test Value T2: 1
Thread 1 : 2
Test Value T1: 4
Thread 2 : 2
Test Value T2: 1
为什么线程2没有读取更新的值?如果它必须是静态的,那么使用volatile?
有人可以链接到一个很好的volatile示例,其中该变量未声明为静态。
谢谢
答案 0 :(得分:4)
使用volatile
是为了确保所有线程同时看到相同值。基本上它是说永远不应该缓存这个变量。
很难在代码中演示,因为缓存策略从cpu变为cpu,从JRE变为JRE。
volatile
对JRE说的是如果您想要缓存此值,因为您认为通过这样做可以使代码运行得更快...... 不要! ...我对编程的了解比你知道得多,而且我毫不怀疑地知道如果你这样做会破坏我的代码。。
答案 1 :(得分:4)
问题是++不是原子的。代码应该使用AtomicInteger
代替。使用static
变量,两个线程都尝试更新相同的值,而++实际上是3个操作:get,+ 1和store。这意味着两个线程之间存在竞争条件,并且它们会相互覆盖。
为什么线程2没有读取更新的值?如果它必须是静态的,那么使用volatile?
static
和volatile
执行不同的操作。 static
使与该类关联的字段与对象实例相对应。 volatile
强制任何字段的读取或写入跨越内存屏障。这允许多个线程读取和更新公共字段,但这样做不保护您免受多个操作的影响。如果你使变量不是是静态的,那么你就不需要volatile
,因为每个线程都会使用它自己的字段实例。
您应该使用AtomicInteger
。这包装了一个volatile int
,但也提供了增量操作的特殊处理:
private static AtomicInteger testValue = new AtomicInteger(1);
...
testValue.incrementAndGet();
有人可以链接到一个很好的volatile示例,其中该变量未声明为静态。
当多个线程共享非静态字段时,您需要volatile
。例如,如果您将testValue
移动到VolatileExample
类,然后将VolatileExample
类的相同实例传递到两个线程中,以便他们可以访问testValue
。
答案 2 :(得分:0)
在这种情况下,当删除了static
修饰符后,您更改了变量,因为现在每个ExampleThread
都有自己的testValue
,因此,每个static
的存储值都不同线程实例。
volatile
修饰符的含义:
有时,您希望拥有所有对象共有的变量。这是通过静态修改器完成的。在声明中具有static修饰符的字段称为静态字段或类变量。它们与类相关联,而不是与任何对象相关联。该类的每个实例共享一个类变量,该变量位于内存中的一个固定位置。任何对象都可以更改类变量的值......
参考:Understanding Instance and Class Members
关于testValue
,如果您尝试其他类型的测试,请将VolatileExample
变量移至类 public class VolatileExample {
private volatile int testValue = 1;
...
}
,如下所示:
testVariable
然后,从ExampleThread
中删除volatile
并运行代码。它的输出应该像第一个一样。
{{1}}修饰符的含义:
使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都会建立与之后读取同一变量的先发生关系。这意味着对volatile变量的更改始终对其他线程可见
答案 3 :(得分:0)
你的上一个问题 &#34;有人可以链接到一个很好的volatile变量示例,其中变量未声明为静态。&#34;为了清楚地理解这个概念,我带来了一个例子。
public class VolatileExample {
// public static volatile int testValue = 1;
public int nonstatic=8;
public static void main(String args[]) {
VolatileExample volatileExample=new VolatileExample();
new ExampleThread("Thread 1 ",volatileExample).start();
new ExampleThread("Thread 2 ",volatileExample).start();
}
}
class ExampleThread extends Thread {
VolatileExample volatileExample;
public ExampleThread(String str,VolatileExample volatileExample){
super(str);
this.volatileExample=volatileExample;
}
public void run() {
for (int i = 0; i < 3; i++) {
try {
if (getName().compareTo("Thread 1 ") == 0)
{
volatileExample.nonstatic++;
System.out.println( "Test Value T1: " + volatileExample.nonstatic);
}
if (getName().compareTo("Thread 2 ") == 0)
{
System.out.println( "Test Value T2: " + volatileExample.nonstatic);
Thread.sleep(1);
}
// Thread.sleep(1000);
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
}
现在请通过公开int nonstatic = 8来查看更改;易失性
以下是挥发时的输出,
测试值T1:9 测试值T1:10 测试值T1:11 测试值T2:11 测试值T2:11 测试值T2:11
处理完成,退出代码为0
如果它是非易失性的,则结果如下: 测试值T1:9 测试值T2:9 测试值T1:10 测试值T1:11 测试值T2:11 测试值T2:11
处理完成,退出代码为0
观察:由于Volatile keyWord使线程直接将其写入主内存,因此线程更快地返回到循环,因此当它变为volatile时,thread1更快地完成其任务并且甚至在thread2开始之前返回到循环
另一方面,使其成为非易失性,使线程更新其locl缓存,然后将其报告给主内存...所以在这个循环中,thread2进入了比赛。
PLZ note,我有&#34;非静态&#34; VolatileExample类中的变量不在ExampleThread中来演示这个变量。
你的另一个问题是: 为什么线程2没有读取更新的值?如果它必须是静态的,那么使用volatile?
好吧,如果你让它的testvalue非静态,那么它只是两个不同的对象,类ExampleThread的Thread1和Thread2工作在他们自己的varibles testvalue ....所以不稳定并不重要..