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问题

    • 添加版本号,把数据更新到主存的时候,再次读取主存中的变量的版本号,如果现在变量的版本号与主存中一致则更新。
    • 添加时间戳。
  • 相关阅读:
    jQuery实现DropDownList(MVC)
    解决MVC应用程序数据重复加载问题
    MVC应用程序使用jQuery接收Url的参数
    openstack上创建vm实例后,状态为ERROR问题解决
    分布式监控系统Zabbix--完整安装记录-批量添加主机和自动发现端口
    LInux下设置账号有效时间 以及 修改用户名(同时修改用户组名和家目录)
    Rsync+Inotify实时同步环境部署记录
    Shell+Curl监控网站页面访问状态,利用SendEmail发邮件通知
    Shell脚本字符串匹配及日常命令工具
    分布式监控系统Zabbix-3.0.3-完整安装记录(6)-微信报警部署
  • 原文地址:https://www.cnblogs.com/zut-syp/p/15362563.html
Copyright © 2011-2022 走看看