【问题标题】:Why modifying a shared variable in one threads affects the other thread even without using volatile?为什么即使不使用 volatile 在一个线程中修改共享变量也会影响另一个线程?
【发布时间】:2012-11-14 10:08:11
【问题描述】:

这里我有三个简单的类:

第 1 类:

public class ThreadSyncMain {

public static int count = 0; // volatile is not use

public static void main(String[] args) {

    Thread thread1 = new Thread( new Thread1(),"Thread1" ); 
    Thread thread2 = new Thread( new Thread2(),"Thread2");

    thread1.start();
    thread2.start();

}

}

第 2 类:

public class Thread1 implements Runnable{
public void run() {

    System.out.println("Thread1 Count :: "+ThreadSyncMain.count);
    ThreadSyncMain.count++;
}

}

第 3 类:

public class Thread2 implements Runnable{
public void run() {

        System.out.println("Thread2 Count :: "+ThreadSyncMain.count);

}
}

输出是:

线程 1 计数 :: 0
线程 2 计数 :: 1

这意味着thread1改变了count的值。那么为什么 thread1 的变化会影响 thread2,因为我没有使用任何“volatile”关键字。在这种情况下,“volatile”关键字不是问题吗?如何修改代码以测试“volatile”?

提前致谢。

更新部分: 在进行了一些命中和试用测试后,我正在更新代码。 1 类保持不变。这是更新的代码:

第 2 类:我添加了 100 毫秒的延迟。

public class Thread1 implements Runnable{
public void run() {

    System.out.println("Thread1 Count :: "+ThreadSyncMain.count);

    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    ThreadSyncMain.count++;
}

}

第 3 类:添加了 while 循环。它里面的计数被持续监控。

public class Thread2 implements Runnable{
public void run() {

    while(true)
    {
        if(ThreadSyncMain.count == 1)
        { 
            System.out.println("Thread2 Count :: "+ThreadSyncMain.count);
        }
    }

}
}

现在在这种情况下,我得到了以下输出:
1.如果class1中没有使用“volatile”,输出为:

 Thread1 Count :: 0

2。如果在 class1 中使用“volatile”,则输出为:

Thread1 Count :: 0
Thread2 Count :: 1
Thread2 Count :: 1
Thread2 Count :: 1
.
.
.

为什么 volatile 在这种情况下会出现?

【问题讨论】:

    标签: java multithreading unit-testing


    【解决方案1】:

    每个线程都有一个内存视图。如果不使用锁,这些视图不能保证在线程之间保持一致。因此,像您在上面所做的那样共享一个变量(没有volatile)将起作用,因为它在线程中可见,但会给您带来不可靠的结果。使用volatile 关键字意味着变量在线程之间被一致读取。

    here,特别注意:

    可变字段是用于通信的特殊字段 线程之间的状态。每次读取 volatile 都会看到最后一次写入 任何线程的 volatile ;实际上,它们是由 程序员作为永远不能看到“陈旧”的领域 缓存或重新排序的结果。

    【讨论】:

    • 感谢您的回复。我已经更新了帖子。所以请您给出更新部分的原因。这是因为我试图区分使用和不使用“volatile”的正确场景。:)
    【解决方案2】:

    Volatile保证线程 1 的副作用对线程 2 可见。没有volatile,更改可能会或可能不会可见。

    测试volatile 的效果很困难,因为它取决于硬件架构、线程实现、编译器优化以及调度程序的确切时间等底层方面。

    如果我这样做,我会编写产生高并发性的多线程测试,并确保我在多处理器实现上运行。然后我可能会观察到有和没有volatile 的代码之间的差异。测试的结果仍然是不确定的。

    【讨论】:

    • 我认为你的最后一句话是关键
    【解决方案3】:

    编译器可能没有缓存计数器,因为它是一个类范围的变量。所以写入在内存中。

    如果您想测试易失性和非易失性写入/读取..

    public class VolatileExperiment
    {
      private int counter ;
      private volatile int volatile_counter;
      public void Counter()
      {
         new Thread( new Runnable(){
         public void run()
         {
            ++counter;
            ++volatile_counter;
            //print
          }
        }).start();
    
    
       new Thread( new Runnable(){
         public void run()
         {
           ++counter;
           ++volatile_counter;
           //print
          }
        }).start();
        //print counter 
       //print volatile 
      }
    

    }

    使用 volatile 可确保编译器不会优化代码,因此写入是在内存中完成的,而不是在线程内存中。所以你应该看到volatile_counter 更新了.. 而counter 可能不会受到影响

    【讨论】:

      猜你喜欢
      • 2021-04-09
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-30
      相关资源
      最近更新 更多