静态变量与易失性

时间:2013-02-25 14:13:26

标签: java multithreading

我只是在线程视角问...可能已经多次回答了,但请帮助我理解这一点。

参考这里的帖子 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示例,其中该变量未声明为静态。

谢谢

4 个答案:

答案 0 :(得分:4)

使用volatile是为了确保所有线程同时看到相同值。基本上它是说永远不应该缓存这个变量。

很难在代码中演示,因为缓存策略从cpu变为cpu,从JRE变为JRE。

volatile对JRE说的是如果您想要缓存此值,因为您认为通过这样做可以使代码运行得更快...... 不要! ...我对编程的了解比你知道得多,而且我毫不怀疑地知道如果你这样做会破坏我的代码。

答案 1 :(得分:4)

问题是++不是原子的。代码应该使用AtomicInteger代替。使用static变量,两个线程都尝试更新相同的值,而++实际上是3个操作:get,+ 1和store。这意味着两个线程之间存在竞争条件,并且它们会相互覆盖。

  

为什么线程2没有读取更新的值?如果它必须是静态的,那么使用volatile?

staticvolatile执行不同的操作。 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变量的更改始终对其他线程可见

参考:Atomic Access

答案 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 ....所以不稳定并不重要..