java - volatile 关键字是否保证读取器线程读取的最新值?如果不是,它的用途是什么?
问题描述
请参考以下代码。
共享.java
class Shared
{
public volatile int i = 0;
}
创建线程.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");
}
}
如果不允许新线程休眠,那么似乎主线程无法找到变量 si 的所有值,因为在这种情况下,我们得到以下输出。
0
15
15
15
..
..
..
如果允许新线程休眠,那么似乎主线程可以找到变量 si 的所有值,因为在这种情况下,我们得到以下输出。
0
0
..
1
1
..
2
2
..
3
3
..
4
4
..
upto 6
从上面的输出可以清楚地看出,如果新线程没有进入睡眠状态,那么新线程在主线程有机会读取它之前,正在多次更改内存中的 si 值。
如果我将程序更改为:
共享.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");
}
}
创建线程.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 的好处吗?
解决方案
volatile 变量在读取和后续写入之间创建发生在边缘之前。因此读取将在同步顺序中看到它之前的最新写入。
给你一个反例。想象一下i
不会不稳定:
while(s.i==false)
System.out.println(s.i);
因为 si 在循环中没有改变,编译器可以像这样将读取提升到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);
}
推荐阅读
- java - 当okhttp从Android中的api获取数据时如何显示加载图标/栏?
- laravel - 虽然更新我的数据没有得到更新 - Laravel 8
- amazon-web-services - 我可以将两个或多个 Web ACL (WAF) 关联到一个 Cloudfront 分配吗?
- django - Heroku 构建 django 应用程序成功,但打开的应用程序抛出错误
- excel - 使用 VBA 从两个现有工作表创建工作表
- postgresql - 计数除零
- reactjs - react-hook-form 输入处理程序耗时太长
- python - 如何为神经网络连接多个矩阵(或重塑)
- r - 如果 r 中小于 0 或大于 0,如何根据条件正确着色 geom_line() 和 geom_point()?
- javascript - 如何停止滚动捕捉过渡的时间