4

这里我有三个简单的类:

第一类:

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”关键字不是问题吗?如何修改代码以测试“易失性”?

提前致谢。

更新部分:我在做一些命中和试用测试后更新代码。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 在这种情况下会出现?

4

3 回答 3

3

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

请参见此处,特别注意:

可变字段是用于在线程之间传递状态的特殊字段。对 volatile 的每次读取都会看到任何线程对该 volatile 的最后一次写入;实际上,它们被程序员指定为由于缓存或重新排序而不能看到“陈旧”值的字段。

于 2012-11-14T10:11:02.243 回答
3

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

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

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

于 2012-11-14T10:15:34.667 回答
1

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

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

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可能不会受到影响

于 2012-11-14T10:14:45.933 回答