问题描述
请参考以下代码。
Shared.java
class Shared
{
public volatile int i = 0;
}
CreateThread.java
class CreateThread
{
public static void main(String[] args) throws InterruptedException
{
Shared s = new Shared();
MyThread t1 = new MyThread(s);
t1.start();
while(s.i!=7)
System.out.println(s.i);
}
}
MyThread.java
class MyThread extends Thread
{
Shared s = null;
MyThread(Shared s)
{
this.s = s;
}
public void run()
{
for(int j=0; j<15; j++)
{
/* try
{
Thread.sleep(1000);
}
catch(Exception e){}*/
s.i = s.i+1;
// System.out.println("New Value of s.i "+s.i);
/* try
{
Thread.sleep(1000);
}
catch(Exception e){}*/
}
System.out.println("Thread Going to Stop");
}
}
如果不允许新线程进入睡眠状态,则主线程似乎无法找到变量s.i的所有值。因为在这种情况下,我们得到以下输出。
0
15
15
15
..
..
..
如果允许新线程进入睡眠状态,则主线程似乎可以找到变量s.i的所有值。因为在这种情况下,我们得到以下输出。
0
0
..
1
1
..
2
2
..
3
3
..
4
4
..
upto 6
从上面的输出中可以清楚地看到,如果新线程没有进入睡眠状态,那么新线程将在内存主线程有机会读取它之前多次更改内存中的si值。
如果我将程序更改为:
Shared.java
class Shared
{
public /*volatile*/ boolean i = false;
}
MyThread.java
class MyThread extends Thread
{
Shared s = null;
MyThread(Shared s)
{
this.s = s;
}
public void run()
{
s.i = true;
System.out.println("New Value of s.i "+s.i);
try
{
Thread.sleep(10000);
}
catch(Exception e){}
System.out.println("Thread Going to Stop");
}
}
CreateThread.java
class CreateThread
{
public static void main(String[] args) throws InterruptedException
{
Shared s = new Shared();
MyThread t1 = new MyThread(s);
t1.start();
while(s.i==false)
System.out.println(s.i);
System.out.println(s.i+" Main Stopped");
}
}
输出:
C:\Users\gyan0\OneDrive\Desktop>java CreateThread
false
New Value of s.i true
true Main Stopped
Thread Going to Stop
即使该数据不是易失性的,它似乎也立即可用于线程。
以下是我的问题。
- volatile关键字是否保证读者线程可以读取最新值?
- 如果volatile关键字通过不将数据保存在cpu寄存器中来将数据保存在内存中,我们将获得什么好处?您可以举一个实际的例子来证明挥发物的好处吗?
解决方法
volatile变量会在读取和后续写入之间的边沿之前发生。因此,读取操作将按照同步顺序看到之前的最新写入操作。
举一个反例。想象i
不会不稳定:
while(s.i==false)
System.out.println(s.i);
由于s.i在循环中没有更改,因此编译器可以像这样从循环中提升i
的读取。
boolean r = s1.i;
while(r == false)
System.out.println(r);
现在,读取将永远不会在循环中看到写入的值。
此代码甚至可以进一步优化:
boolean r = s1.i;
if(r==false){
while(true)
System.out.println(false);
}
,
关于您的问题2,所有线程都有其本地缓存,这些缓存可能不会立即与主内存进行更新。使用volatile,可以确保更新值,并且线程可以获取更新值。您可以检查以下示例:
https://dzone.com/articles/java-multi-threading-volatile-variables-happens-be-1