zoukankan      html  css  js  c++  java
  • 【Java并发编程学习笔记】volatile关键字

    volatile关键字

          volatile关键字是JVM提供的一个轻量级的同步机制,可以保证有序性和可见性,不能保证原子性。

    volatile原理

        volatile修饰的变量带有内存屏障(写屏障/读屏障),Mermory Barrier(Mermory Fence)

    volatile保证可见性和有序性

    可见性

        volatile修饰的变量是带有写屏障和读屏障的,写屏障之前对于共享变量的修改都刷新到主存中。在读屏障之后,读取的数据都是从主存中加载进来的最新数据,每次读取的时候都从主存读取出来生成一个新的副本。

    public class Main {
        static volatile boolean flag = true;
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(new Runnable() {
                public void run() {
                    // 读屏障,在此之后从主存之中读取共享变量的最新值
                    while (flag) {
                        
                    }
                }
            });
            Thread.sleep(2000);
            t1.start();
            Thread t2 = new Thread(new Runnable() {
                public void run() {
                    flag = false;
                    // 写屏障,在此之前将共享变量的值刷新到主存中
                }
            });
            t2.start();
        }
    }

    有序性

        volatile遵循happened-befores规则:多线程情况下,即便发生了指令重排序也不影响最终的结果。

    synchronized&volatile

    • volatile关键字是线程同步的轻量级实现,性能要比synchronized
    • volatile只能修饰变量;synchronized可以修饰方法和同步代码块
    • JDK新版本的发布优化了synchronized,优化后的synchronized执行效率有了很大的提升,一般在开发中使用synchronized
    • 多线程反问volatile不会阻塞,synchronized会阻塞
    • volatile不能保证原子性,可以保证可见性和有序性;synchronized三个核心问题都可以解决
    • volatile解决的是多个线程之间共享数据的可见性;synchronized解决的是多线程之间访问共享资源的同步性

    DCL+volatile

    // Double Check Lock
    public class Singleton {
        //private static volatile Singleton instance;
        private static Singleton instance;
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        // new不是原子操作,所以有可能引发多线程问题
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    
        public static void main(String[] args) {
            Singleton singleton = Singleton.getInstance();
        }
    }
    • new操作不是原子操作,这一点我们可以通过查看字节码文件来得知
    • 查看字节码文件:idea安装jclasslib插件,view->show Bytecode with jclasslib
    // 指令含义查看字节码手册
    17 new #3 <Singleton> 
    // 1. 在堆区分配内存,生成一个不完全的对象,
    // 将不完全对象的引入压入栈顶
    20 dup 
    // 1. 复制栈顶元素, 将复制数组压入栈顶
    21 invokespecial #4 <Singleton.<init> : ()V>
    // 堆区中的对象就是一个完整的对象了(执行了默认构造方法)
    24 putstatic #2 <Singleton.instance : LSingleton;>
    // 将完整对象的引用赋值给方法区的共享变量
    
    正常流程(上面的流程) // 1.生成一个不完全对象(创建一个对象空间) // 2.初始化对象 // 3.instance指向初始化对象 // 4.单线程访问对象没有问题
    如果发生指令重排 // 1.首先创建一个不完全对象(创建一个对象空间) // 2.instance指向这个对象空间 // 3.初始化对象 // 4.单线程访问对象的时候不会影响结果 // 问题:多线程情况下,可能在当前线程在创建对象的过程中, // 由于指令重排序使得其他的线程获取到instance==null,此时instance只对自己的线程可见, // 那么竞争到锁以后,instance依然为null,进而无法保证得到的对象是单例的,此时就需要添加volatile

    CAS

    • Unsafe实现了CAS操作(Java的原子操作类中使用了Unsafe)。
    • Java代码中,Unsafe类只能通过反射来获取。
    • CAS可以将read-modify-write这类的操作转化为原子操作。
    • 悲观锁:synchronized可以称为悲观锁,每次只允许一个线程执行同步代码块,其他线程只能阻塞。
    • 乐观锁:CAS可以称为乐观锁,允许多个线程同步操作共享资源。

    CAS+volatile

    • CAS+volatile可以实现无锁化编程
      • CAS在操作共享变量的时候,如果使用volatile修饰共享变量,可以保证共享变量的可见性。
      • 无锁化编程适用于竞争不激烈,多核CPU的情况之下:
      • 线程可以并发,不会进入阻塞或者等待状态,可以提高执行的效率;
      • 当时当竞争激烈的时候,那么比较的次数就会变多,重试的次数也会变多,CPU占用也就越多,反而影响效率。

    CAS解决ABA问题

    • 添加版本号,把数据更新到主存的时候,再次读取主存中的变量的版本号,如果现在变量的版本号与主存中一致则更新。
    • 添加时间戳。
  • 相关阅读:
    搜索回车跳转页面
    登录验证码
    【排序算法】排序算法之插入排序
    PAT 乙级 1044 火星数字 (20 分)
    PAT 甲级 1035 Password (20 分)
    PAT 甲级 1041 Be Unique (20 分)
    PAT 甲级 1054 The Dominant Color (20 分)
    PAT 甲级 1027 Colors in Mars (20 分)
    PAT 甲级 1083 List Grades (25 分)
    PAT 甲级 1005 Spell It Right (20 分)
  • 原文地址:https://www.cnblogs.com/zut-syp/p/15362563.html
Copyright © 2011-2022 走看看