使用 volatile 关键字

发布于 2024-08-29 04:12:39 字数 1150 浏览 8 评论 0原文

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

但在下面的程序中,易失性和非易失性变量都显示相同的值。

第二个线程的 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

As i understand, if we declare a variable as volatile, then it will not be stored in the local cache. Whenever thread are updating the values, it is updated to the main memory. So, other threads can access the updated value.

But in the following program both volatile and non-volatile variables are displaying same value.

The volatile variable is not updated for the second thread.
Can anybody plz explain this why testValue is not changed.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

疯狂的代价 2024-09-05 04:12:39

您的变量仅限于单个线程,因此没有其他线程访问它们。因此,易失性没有什么区别。

如果您将它们声明为静态,它们将在不同线程之间共享。但是,即使如此,您也可能无法观察 易失性变量和非易失性变量之间的差异。引自 Java 并发实践,第 1 章。 3.1.4:

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

在您的情况下,代码碰巧首先修改 易失性变量,因此另一个变量的更新值可能对其他线程不可见。到目前为止,一切都很好。

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

更新2:尝试这个修改版本(注意:我还没有测试过):

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章) :

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

Your variables are confined to a single thread, so there is no other thread accessing them. Thus volatile makes no difference.

If you declared them static, they would be shared between different threads. However, even then you may not be able to observe the difference between your volatile and nonvolatile variable. Quote from Java Concurrency in Practice, chap. 3.1.4:

The visibility effects of volatile variables extend beyond the value of the volatile variable itself. When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable. So from a memory visibility perspective, writing a volatile variable is like exiting a synchronized block and reading a volatile variable is like entering a synchronized block.

In your case, the code happens to modify the volatile variable first, so the updated value of the other variable may not be visible to the other thread. So far, so good.

However, since you are printing out the values of the variables from the same thread which modified them, you won't see any difference anyway.

Update2: Try this modified version (note: I have not tested it):

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();
    }
}

Update: regarding the visibility of static fields - again from the same tome (chap 16.2.3):

[...] statically initialized objects require no explicit synchronization either during construction or when being referenced. However, this applies only to the as-constructed state - if the object is mutable, synchronization is still required by both readers and writers to make subsequent modifications visible and to avoid data corruption.

空城之時有危險 2024-09-05 04:12:39

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

This has nothing to do with volatile; those are two separate instances of ExampleThread, with their own copies of testValue1 and testValue, which are instance fields (not static class variables, which are "shared" between all instances).

温柔戏命师 2024-09-05 04:12:39

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

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

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

ExampleThread 1 and ExampleThread 2 are different objects.

In one of them you assigned 10 to both int fields and that's why you see that output for first thread.

In second you didn't assign anything to int fields so you get 0s.

提笔落墨 2024-09-05 04:12:39

您可能丢失了static关键字吗?

May be you lost static keyword?

迟月 2024-09-05 04:12:39

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

使testValue静态和易变才会有效果。但是,您可能不会(也可能不会)看到这种效果,因为它高度依赖于时间、调度和缓存策略,而这些策略超出了您(甚至虚拟机)的控制范围。缺少 volatility 很少会产生影响,这使得此类错误很难被发现。仅当一个线程更新该值并且第二个线程读取该值并且该值仍在两个线程中的任何一个的缓存中时才会看到它。

testValue is a member variable, so the two threads see two independent copies. volatile is relevant when two or more threads have a reference to the same object.

Make testValue static and volatile will have an effect. However, you may not (and probably will not) see that effect, as it is highly dependent on timing, scheduling and caching strategies which are out of your (or even the VM's) control. A missing volatile will only rarely have an effect, which makes such bugs very hard to catch. It will only be seen when a thread updates the value and the second thread reads the value and the value is still in the cache in either of the two threads.

陌伤浅笑 2024-09-05 04:12:39

下面是一个示例,显示由两个线程访问的变量。 StarterThread 线程在线程启动时设置变量startedWaiterThread 等待变量 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 不是易失性的,则没有同步点可以保证 WaiterThread 将获得 started 变量的更新值。因此,WaiterThread 线程可能会“无限期”运行。

Here is an example showing a variable that is accessed by two threads. The StarterThread thread sets the variable started when the thread starts. The WaiterThread waits for the variable started to be set.

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();
    }
}

If started is not volatile, then there is no synchronization point that guarantees that WaiterThread will ever get the updated value of the started variable. Consequently, the WaiterThread thread could potentially run "indefinitely".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文