首页 > 解决方案 > 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

似乎数据,即使它不是易失性的,也可以立即供线程使用。

以下是我的问题。

  1. volatile 关键字是否保证读取器线程读取的最新值?
  2. 如果 volatile 关键字通过不将数据保存在 CPU 寄存器中而将数据保存在内存中,我们将获得什么好处?你能举一个实际的例子来证明 volatile 的好处吗?

标签: javamultithreadingvolatilethread-synchronization

解决方案


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

推荐阅读