繁体   English   中英

使用volatile关键字

[英]using volatile keyword

据我所知,如果我们将变量声明为volatile,那么它将不会存储在本地缓存中。 每当线程更新值时,它都会更新到主存储器。 因此,其他线程可以访问更新的值。

但是在下面的程序中,volatile和non-volatile变量都显示相同的值。

不会为第二个线程更新volatile变量。 任何人都可以解释为什么testValue没有改变。

class ExampleThread extends Thread {
    private int testValue1;
    private volatile int testValue;
    public ExampleThread(String str){
      super(str);
    }
    public void run() {
    if (getName().equals("Thread 1 "))
    {
        testValue = 10;
        testValue1= 10;
        System.out.println( "Thread 1 testValue1 : " + testValue1);
        System.out.println( "Thread 1 testValue : " + testValue);
    }
    if (getName().equals("Thread 2 "))
    {
        System.out.println( "Thread 2 testValue1 : " + testValue1);
        System.out.println( "Thread 2 testValue : " + testValue);
    }               
}
}

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


output:
Thread 1 testValue1 : 10
Thread 1 testValue : 10
Thread 2 testValue1 : 0
Thread 2 testValue : 0

您的变量仅限于一个线程,因此没有其他线程访问它们。 因此, volatile没有区别。

如果您将它们声明为static ,则它们将在不同的线程之间共享。 但是,即使这样,您也可能无法观察到易失性和非易失性变量之间的差异。 引自Java Concurrency in Practice ,第二章。 3.1.4:

volatile变量的可见性效果超出了volatile变量本身的值。 当线程A写入易失性变量并且随后线程B读取相同的变量时,在写入易失性变量之前,A可见的所有变量的值在读取volatile变量后变为B可见。 因此,从内存可见性的角度来看,编写volatile变量就像退出synchronized块一样,读取volatile变量就像进入synchronized块一样。

在您的情况下,代码首先修改volatile变量,因此另一个变量的更新值可能对另一个线程不可见。 到现在为止还挺好。

但是,由于您要从修改它们的同一个线程中打印出变量的值,因此无论如何都不会看到任何差异。

Update2:试试这个修改过的版本(注意:我还没有测试过):

class ExampleThread extends Thread {
    private static int testValue1;
    private static volatile int testValue;
    private int newValue;

    public ExampleThread(String str, int newValue){
      super(str);
      this.newValue = newValue;
    }
    public void run() {
      for (int i = 0; i < 10; i++) {
        System.out.println(getName() + " testValue1 before update: " + testValue1);
        System.out.println(getName() + " testValue before update: " + testValue);
        testValue = i * newValue;
        testValue1 = i * newValue;
        System.out.println(getName() + " testValue1 after update: " + testValue1);
        System.out.println(getName() + " testValue after update: " + testValue);
        sleep(10);
      }               
    }               
}

public class VolatileExample {
    public static void main(String args[]) {
        new ExampleThread("Thread 1 ", 5).start();
        new ExampleThread("Thread 2 ", 10).start();
    }
}

更新:关于静态字段的可见性 - 再次来自同一个主题(第16.2.3节):

[...]静态初始化的对象在构造期间或被引用时不需要显式同步。 但是,这仅适用于构造状态 - 如果对象是可变的,则读取器和写入器仍然需要同步以使后续修改可见并避免数据损坏。

这与volatile无关; 这些是ExampleThread两个独立实例,它们有自己的testValue1testValue副本,它们是实例字段(不是static类变量,它们在所有实例之间“共享”)。

ExampleThread 1和ExampleThread 2是不同的对象。

在其中一个中,您为两个int字段分配了10,这就是您看到第一个线程的输出的原因。

在第二个你没有为int字段分配任何东西,所以你得到0。

testValue是一个成员变量,因此两个线程看到两个独立的副本。 当两个或多个线程具有对同一对象的引用时, volatile是相关的。

使testValue静态和volatile会产生影响。 但是,您可能不会(也可能不会)看到这种效果,因为它高度依赖于您(甚至是VM)控件之外的时序,调度和缓存策略。 丢失的volatile只会很少产生影响,这使得这些bug很难捕获。 只有当一个线程更新该值并且第二个线程读取该值并且该值仍然在两个线程中的任何一个中的缓存中时,才会看到它。

可能你丢失了静态关键字?

这是一个示例,显示了由两个线程访问的变量。 StarterThread线程设置变量started线程启动时。 WaiterThread等待started设置的变量。

public class Main
{
    static /*volatile*/ boolean started = false;

    private static class StarterThread extends Thread
    {
        public void run()
        {
            started = true;
        }
    }

    private static class WaiterThread extends Thread
    {
        public void run()
        {
            while (!started)
            {
            }
        }
    }

    public static void main(String[] args)
    {
        new StarterThread().start();
        new WaiterThread().start();
    }
}

如果started不是volatile,则没有同步点可以保证WaiterThread将获得已started变量的更新值。 因此, WaiterThread线程可能“无限期地”运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM