Volatile关键字详解

文章目录

      • 一. volatile 关键字代码示例
      • 二. volatile 关键字的作用
      • 三. volatile 关键字原理和实现机制
      • 四. volatile 关键字的使用场景
      • 五. volatile与synchronized的区别

一. volatile 关键字代码示例

如下的代码没有给局部变量 volatile 修饰

import java.util.concurrent.TimeUnit;

/**
 * 类名称:ReaderAndUpdater
 * 类描述:TODO
 *
 * @author: 
 * 创建时间:2019/8/25 9:58
 * Version 1.0
 */
public class ReaderAndUpdater {

    final static int MAX=5;
    static int init_value=0;

    public static void main(String[] args){

        new Thread(()->{

            //定义此线程的局部变量, 把成员变量的值, 赋值给局部变量
            int localValue = init_value;

            //当局部变量小于循环的最大值的时,才进入循环内
            while (localValue<MAX) {

                // 当局部变量不等于成员变量的时候, 才打印局部变量
                if (localValue != init_value) {
                    System.out.println("Reader: "+ init_value);

                    // 把成员变量的值, 赋值给局部变量.
                    // 此时只有当局部变量不等于成员变量的时候,才会进行打印成员变量
                    localValue= init_value;
                }

            }

        },"Reader").start();


        new Thread(()->{

            //定义此线程的局部变量
            int localValue = init_value;

            //当局部变量小于循环的最大值的时,才进入循环内
            while (localValue < MAX) {

                //打印局部变量的值, 先加, 再打印
                System.out.println("update: "+(++localValue));

                // 把加1之后的局部变量的值, 赋值给成员变量
                init_value = localValue;

                try {
                    // 休眠线程1s, 为了让线程切换到读线程
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Update").start();
    }
}

运行效果如图:
Volatile关键字详解_第1张图片
给init_value 变量进行 volatile 修饰
static volatile int init_value=0;
运行效果如下图:
可以看到执行一次更新线程, 就执行一次读取的线程.
Volatile关键字详解_第2张图片

二. volatile 关键字的作用

volatile 关键字的作用: 让其他线程,能够马上感知到某一线程对某个变量的修改.

  1. 保证可见性
    对共享变量的修改, 其他的线程马上能感知到.
    但是不能保证原子性, 读 写 i++
  2. 保证有序性
    重排序, 在编译阶段, 指令优化阶段. 提高代码的执行效率 . CPU的流水线 提高cpu的吞吐量.
    输入程序的代码顺序并不是实际执行的顺序, 重新排序后, 对单线程没有影响, 对多线程有影响.

对于被volatile修饰的变量,遵循三个原则
(1)volatile之前的代码不能调整到他的后面
(2)volatile之后的代码不能调整到他的前面(as if seria)
(3)被volatile修饰的变量位置不变化

三. volatile 关键字原理和实现机制

可以使用HSDIS, 查看到java文件到 class文件, 再到JVM文件, 最后到ASM文件的过程.

被volatile 修饰的变量, 实际上是给这个变量加锁了.
例如 volatile int a ;
实际上为 Lock a ;

加锁后, 如果修改了该变量, 那么不能从工作空间中寻找值, 而是应该从主存中寻找值.

四. volatile 关键字的使用场景

  1. 状态标志 即开关模式

示例代码
在run方法中, 只有当started 标志位为true的时候, 才会执行dowork的方法
当执行shurdown方法的 时候, 会把标志位置为false.

public class ShutDowsnDemmo extends Thread{
    private volatile boolean started=false;

    @Override
    public void run() {
        while(started){
            dowork();
        }
    }
    public void shutdown(){
        started=false;
    }
}
  1. 双重检查锁定(double-checked-locking)
public class Singleton {

    private volatile static Singleton instance;

    // 饿汉式
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }

}
  1. 需要利用顺序性
    当某个变量必须执行在某段代码之前的时候.

五. volatile与synchronized的区别

(1)使用上的区别
Volatile只能修饰变量,synchronized只能修饰方法和语句块
(2)对原子性的保证
synchronized可以保证原子性,Volatile不能保证原子性
(3)对可见性的保证
都可以保证可见性,但实现原理不同
Volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM
(4)对有序性的保证
Volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行
(5)其他
synchronized引起阻塞
Volatile不会引起阻塞

你可能感兴趣的:(并发编程)