Java 为什么即使不使用volatile,修改一个线程中的共享变量也会影响另一个线程?
这里我有三个简单的类: 第1类: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()
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”的正确场景:)