JAVA并发编程之-Volatile关键字及内存可见性

做者:毕来生
微信:878799579

1. 什么是JUC?

JUC全称 java.util.concurrent 是在并发编程中很经常使用的实用工具类java

2.Volatile关键字

一、若是一个变量被volatile关键字修饰,那么这个变量对全部线程都是可见的。
二、若是某条线程修改了被Volatile修饰的这个变量值,修改后的值对于其余线程来时是当即可见的。
三、并非通过Volatile修饰过的变量在多线程下就是安全的
四、多线程间可使用SynchronousQueue或者Exchanger进行数据之间传递编程

3.内存可见性

内存可见性(Memory Visibility)是指当某个线程正在使用对象状态 而另外一个线程在同时修改该状态,须要确保当一个线程修改了对象 状态后,其余线程可以看到发生的状态变化。
可见性错误是指当读操做与写操做在不一样的线程中执行时,咱们没法确保执行读操做的线程能适时地看到其余线程写入的值,有时甚至是根本不可能的事情。
原理同CAS原理相同,不懂的同窗能够自行百度,附上一张CAS演示图供你们参考缓存

在这里插入图片描述

4.实战举例

经过线程来修改变量count的值,使用Volatile关键字修饰和不使用Volatile修饰count变量结果对比。安全

首先咱们来看一下经过内部类实现Runnable,变量<font color="red">使用Volatile关键字</font>修饰演示以及结果微信

package org.bilaisheng.juc;

/**
 * @Author: bilaisheng
 * @Wechat: 878799579
 * @Date: 2019/1/1 16:29
 * @Todo: 经过内部类实现Runnable,变量使用Volatile关键字修饰演示
 * @Version : JDK11 , IDEA2018
 */
public class NoVolatileTest{

    public static void main(String[] args) {
        NoVolatileThread noVolatileThread = new NoVolatileThread();
        new Thread(noVolatileThread).start();

        while (true){
            if(noVolatileThread.isFlag()){
                System.out.println("flag 此时为true !");
                break;
            }
        }
    }
}

class NoVolatileThread implements Runnable{

    private boolean flag = false;
    
    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        flag = true;

        System.out.println(Thread.currentThread().getName() + " flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

运行结果以下图所示:多线程

在这里插入图片描述


接下来咱们来看一下经过内部类实现Runnable,变量<font color="red">不使用Volatile关键字</font>修饰演示以及结果并发

package org.bilaisheng.juc;

/**
 * @Author: bilaisheng
 * @Wechat: 878799579
 * @Date: 2019/1/1 16:53
 * @Todo: 经过内部类实现Runnable,变量使用Volatile关键字修饰演示
 * @Version : JDK11 , IDEA2018
 */
public class VolatileTest{

    public static void main(String[] args) {
        VolatileThread volatileThread = new VolatileThread();
        new Thread(volatileThread).start();

        while (true){
            // if的判断volatile保证当时确实正确,而后线程a可能处于休眠状态,
            // 线程b也判断不存在,b线程就new了一个。
            // 而后a线程wake up,据需执行new volatile获取最新值。
            if(volatileThread.isFlag()){
                System.out.println("flag 此时为true !");
                break;
            }
        }
    }
}

class VolatileThread implements Runnable{

    private volatile boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;

        System.out.println(Thread.currentThread().getName() + " flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

运行结果以下图所示:
在这里插入图片描述ide

经过对比咱们发如今经过Volatile修饰和不经过Volatile修饰的变量,输出结果居然会有些误差。究竟是为何呢?高并发

咱们逐步拆解上面代码执行步骤:工具

一、针对于不使用Volatile关键字修饰变量:

  • 步骤一:默认flag = false;
  • 步骤二main线程的缓存区域没有刷新 flag的值。因此flag 仍是false。故没有输出<flag 此时为true !>
  • 步骤三:子线程输出 Thread-0 flag = true

二、针对于使用Volatile关键字修饰变量:

  • 步骤一:默认flag = false;
  • 步骤二:主线程看到flag是被Volatile关键字修饰的变量。则获取最新的flag变量值,此时flag = true。故输出<flag 此时为true !>
  • 步骤三:子线程输出 Thread-0 flag = true

5. Volatile的优势

可见性:被Volatile修饰的变量能够立刻刷新主内存中的值,保证其余线程在获取时能够获取最新值,全部线程看到该变量的值均相同。

轻量级的synchronized,高并发下保证变量的可见性。


6.Volatile的缺点

一、频繁刷新主内存中变量,可能会形成性能瓶颈

二、不具有操做的原子性,不适合在对该变量的写操做依赖于变量自己本身。例如i++,并不能经过volatile来保证原子性

7.喜欢就关注我吧

在这里插入图片描述

相关文章
相关标签/搜索