多线程之volatile关键字

Posted Trigl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程之volatile关键字相关的知识,希望对你有一定的参考价值。

之前讲解了多线程的synchronized关键字,现在再学习一下volatile关键字。

1 volatile关键字用来做什么?

线程安全包括两个方面:原子性和可见性。Java的同步机制都是围绕这两个方面来确保线程安全的。其中原子性是指多个动作必须依次连续完成,必须保持一致性和完整性,这可以通过synchronized关键字来实现;可见性是指变量在多个线程之间是可见的,即如果其中一个线程改变了变量的值,另一个线程可以看到改变后的值,而不会引起脏读,这就是我们的主角volatile关键字的作用:使变量在多个线程间可见(实际上synchronized也可以间接保证可见性,后面讲解)。
首先看一个不加volatile关键字的栗子

package com.trigl.concurrent.learnvolatile;

public class RunThread implements Runnable 
    private boolean isRunning = true;

    public boolean isRunning() 
        return isRunning;
    

    public void setRunning(boolean isRunning) 
        this.isRunning = isRunning;
    

    @Override
    public void run() 
        System.out.println("进入run了");
        while (isRunning) 

        
        System.out.println("线程被停止了!");
    

    public static void main(String[] args) throws InterruptedException 
        RunThread thread = new RunThread();
        new Thread(thread).start();
        Thread.sleep(100);
        thread.setRunning(false);
        System.out.println("已经赋值为false");
    

结果:

进入run了
已经赋值为false

实例中的线程有一个通过判断isRunning的真假实现的循环,默认是true,我们在测试方法中将isRunning改为了false,但是根据结果显示循环仍然在继续,也就是我们赋的值并没有起作用。
现在在isRunning的声明前面加上volatile关键字再试试:

volatile private boolean isRunning = true;

结果:

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

可以看到这次循环就被停止了,我们的赋值操作起作用了,volatile到底怎么实现的呢?
在启动RunThread.java线程时,变量

private boolean isRunning = true;

存在于公共堆栈和线程的私有堆栈两个地方,为了提升效率,线程一直在默认堆栈中取值,所以取得的isRunning一直是true;而代码

thread.setRunning(false);

更新的却是公共堆栈中isRunning的值,因此这就造成了公共堆栈中isRunning的值是false而线程持有的私有堆栈中isRunning的值却是true,所以没有volatile关键字时线程是死循环状态。
这样的问题其实就是私有堆栈中的值和公共堆栈中的值不同步造成的,解决这样的问题就要用volatile关键字了,它主要的作用就是当线程访问isRunning这个变量时,强制性从公共堆栈中取值。
使用volatile关键字增加了实例变量在多个线程之间的可见性,但volatile关键字最致命的缺点是不支持原子性。

2 synchronized同步代码块也可以实现volatile的功能

关键字synchronized可以使多个线程访问同一个资源时具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能,从而可以实现类似于volatile的功能,上栗子验证。
还是不上新栗子了,仍然用上面的例子来举例,在循环代码里面添加同步代码块,完整代码如下:

package com.trigl.concurrent.learnvolatile;

public class RunThread implements Runnable 
    private boolean isRunning = true;

    public boolean isRunning() 
        return isRunning;
    

    public void setRunning(boolean isRunning) 
        this.isRunning = isRunning;
    

    @Override
    public void run() 
        System.out.println("进入run了");
        while (isRunning) 
            // 在这添加同步代码块
            synchronized ("任意字符") 

            
        
        System.out.println("线程被停止了!");
    

    public static void main(String[] args) throws InterruptedException 
        RunThread thread = new RunThread();
        new Thread(thread).start();
        Thread.sleep(100);
        thread.setRunning(false);
        System.out.println("已经赋值为false");
    

结果为:

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

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

3 synchronized和volatile的区别

  • 关键字volatile是线程同步的轻量级实现,所有volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法以及代码块。随着JDK新版本的发布,synchronized在执行效率上得到了很大的提升,在开发中使用synchronized的比率还是比较大的。
  • 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
  • volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,以为它会将私有内存和公共内存中的数据做同步。
  • 最后再次强调,volatile解决的是变量在多个线程之间的可见性;而synchronized解决的是多个线程之间访问资源的同步性。

OVER

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于多线程之volatile关键字的主要内容,如果未能解决你的问题,请参考以下文章

多线程之volatile关键字

java多线程之volatile讲解

多线程之volatile关键字

Java——多线程高并发系列之volatile关键字

Java——多线程高并发系列之volatile关键字

Java多线程之synchronized和volatile的比较