Java 为什么即使不使用volatile,修改一个线程中的共享变量也会影响另一个线程?

Java 为什么即使不使用volatile,修改一个线程中的共享变量也会影响另一个线程?,java,multithreading,unit-testing,Java,Multithreading,Unit Testing,这里我有三个简单的类: 第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()

这里我有三个简单的类:

第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);

}
}
输出为:

 Thread1 Count :: 0
Thread1 Count :: 0
Thread2 Count :: 1
Thread2 Count :: 1
Thread2 Count :: 1
.
.
.
线程1计数::0
线程2计数::1

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

提前谢谢

更新部分: 我在做了一些点击测试和试用测试之后正在更新代码。第一类保持不变。以下是更新的代码:

第二类:我增加了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.如果在1类中未使用“volatile”,则输出为:

 Thread1 Count :: 0
Thread1 Count :: 0
Thread2 Count :: 1
Thread2 Count :: 1
Thread2 Count :: 1
.
.
.
二,。如果在1类中使用“volatile”,则输出为:

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

为什么volatile会出现在这个场景中?

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

请参见,并特别注意:

易失性字段是用于通信的特殊字段 线程之间的状态。volatile的每次读取都将看到最后一次写入 任何一根线都能使它变得易变;实际上,它们是由 程序员作为字段,对于这些字段,永远不能接受“过时” 值作为缓存或重新排序的结果


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

如果要测试易失性和非易失性写入/读取

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\u计数器已更新。。虽然
计数器
可能不受影响

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

测试
volatile
的效果很困难,因为它依赖于底层方面,如硬件体系结构、线程实现、编译器优化,甚至调度器的精确定时


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

我想你的最后一句话很关键,谢谢你的回答。我已经更新了帖子。因此,请您给出更新零件的原因。这是因为我试图区分使用和不使用“volatile”的正确场景:)