Fork me on GitHub
返回顶部
跳到底部

volatile关键字

volatile关键字

在java2以前,Java的内存模型总是从主存(共享内存)读取变量,而在当前的Java内存模型下,每个线程把变量保存到本地内存中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另一个线程还在继续使用它在本地内存中的值。这个问题就是变量在多线程中的可见性问题。

要解决这个问题,就需要把变量生命为volatile,这就指示JVM,这个变量是不稳定的,每个线程访问该变量,它都到主内存中读取,而且,当变量变化时,强迫线程将变化值回写到主存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值,这样也保证了变量在多线程之间的可见性。

看下面一个例子:

public class VolatileTest extends Thread {

    private static boolean flag =true;
    @Override
    public void run() {
        System.out.println("进入run了");
        while (flag == true) {}
        System.out.println("线程被停止了!");
    }

    public static void main(String[] args) {
        VolatileTest volatileTest=new VolatileTest();
        volatileTest.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        volatileTest.flag=false;
        System.out.println("已经将isRunning赋值为false");
    }
}

运行结果:

进入run了
已经将isRunning赋值为false

可以看到没有“输出线程被停止!”。线程进入了死循环。
这是因为main线程和volatileTest线程各自有一个工作线程,里面有变量flag的拷贝,flag在这两个线程之间不可见,即main线程修改了自己工作内存中flag的值,volatileTest线程并不知道。

现在我们把flag前面加上关键字volatile,如下:

volatile private static  boolean flag =true;

输出就变成下面:

进入run了
已经将isRunning赋值为false
线程被停止了!

这是因为volatile保证了变量在不同线程之间的可见性。

如果我们不想给flag前家volatile关键字,也可以通过下面的方式解决:在whiel循环里任意加上一个输出语句或将该线程sleep一下。

volatile private static  boolean flag =true;
    @Override
    public void run() {
        System.out.println("进入run了");
        while (flag == true) {
            //法1:加一个循环语句
            System.out.println("hello world");
            //法2:将当前线程sleep一下
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }
        System.out.println("线程被停止了!");
    }

为什么呢?
因为JVM会尽力保证变量的可见性,即便这个变量没有加volatile关键字只要CPU有时间,JVM就会尽力去保证变量值的更新。这与volatile关键字的不同之处在于,volatile会强制保证变量的可见性;而不加volatile关键字,JVM只会尽力去保证变量的可见性,如果CPU一直有其它的事情处理,它也就不会去保证了。最开始,CPU一直处于占用状态,JVM不会强制CPU其主存中取变量的最新值,所以死循环。后来加入输出语句或sleep线程,CPU就有时间去保证变量的可见性,即从主内存中去变量flag的值。

问题:为什么加入输出语句,CPU就有时间了?我也在继续学习探究。

synchronized和volatile的比较

  • volatile是保证数据在多线程之间的可见性的,而synchronized是解决资源在多线程之间的同步,同一时间,只有一个线程会获取资源。
  • 多线程访问volatile关键字不会发生阻塞,而synchronized会发生阻塞。
  • volatile只能修饰变量,synchronized可以修饰方法,代码块。
  • synchronized在Java6之后引入了偏向锁和轻量级锁,减少获得锁和释放锁带来的性能消耗。
posted @ 2018-05-16 12:33  sqmax  阅读(186)  评论(0编辑  收藏  举报