• volatile的使用


    作用

    1. 可见性
    2. 有序性

    内存语义

    当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中,当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量。所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取。

    底层实现

    内存屏障

    使用场景

    1. 普通变量赋值可以,但是含复合运算的赋值不行(类似i++),也不能修饰引用类型,不能保证对象内部的属性可见性。

      volatile int a = 10; // 可以
      volatile DeadlockDemo demo = new DeadlockDemo(); // 不可以
      
    2. 状态标志,判断业务是否结束

      public class VolatileDemo {
        private static volatile boolean flag = true;
        public static void main(String[] args) throws InterruptedException {
            new Thread(()->{
                while (flag){
                    System.out.println("flag的值为:"+flag);
                }
            },"t1").start();
            new Thread(()->{
                flag = false;
                System.out.println("另一个线程更改flag为false了");
            },"t2").start();
        }
      
    3. 开销较低的读,写锁策略

    	/**
         * 使用:当读远多于写,结合使用内部锁和 volatile 变量来减少同步的开销
         * 理由:利用volatile保证读取操作的可见性;利用synchronized保证复合操作的原子性
         */
        public class Counter {
            private volatile int value;
    
            public int getValue() {
                return value;   //利用volatile保证读取操作的可见性
            }
    
            public synchronized int increment() {
                return value++; //利用synchronized保证复合操作的原子性
            }
        }
    
    1. 双端锁的单例模式

      public class SingletonDemo {
          // volatile防止指令重排序,内存可见(缓存中的变化及时刷到主存,并且其他的内存失效,必须从主存获取)
          private volatile SingletonDemo instance = null;
      
          private SingletonDemo() {
              //构造器必须私有  不然直接new就可以创建
          }
      
          public SingletonDemo getInstance() {
              //第一次判断,假设会有好多线程,如果 SingletonDemo 没有被实例化,那么就会到下一步获取锁,只有一个能获取到,
              //如果已经实例化,那么直接返回了,减少除了初始化时之外的所有锁获取等待过程
              if (instance == null) {
                  synchronized (SingletonDemo.class) {
                      //第二次判断是因为假设有两个线程A、B,两个同时通过了第一个if,然后A获取了锁,进入然后判断 instance 是null,他就实例化了instance,然后他出了锁,
                      //这时候线程B经过等待A释放的锁,B获取锁了,如果没有第二个判断,那么他还是会去new SingletonDemo(),再创建一个实例,所以为了防止这种情况,需要第二次判断
                      if (instance == null) {
                          instance = new SingletonDemo();
                      }
                  }
              }
              return instance;
          }
      }
      

      扩展:除了双端锁的单例模式,还有一种安全的单例模式就是静态内部类

      //现在比较好的做法就是采用静态内部内的方式实现
      class SingletonDemo2 {
          private SingletonDemo2() {
          }
      
          private static class SingletonDemoHandler {
              private static SingletonDemo2 instance = new SingletonDemo2();
          }
      
          public static SingletonDemo2 getInstance() {
              return SingletonDemoHandler.instance;
          }
      } 
      
    2. 对象的属性修改原子类(AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater)

    更新的对象属性必须使用 public volatile 修饰符。

    使用目的:以一种线程安全的方式操作非线程安全对象内的某些字段

  • 相关阅读:
    discuz开发笔记
    响应式布局
    timedelta
    图片轮播
    性能
    事件捕获
    git
    css hacks
    AFNetworking 网络错误提示data转换字符串
    常见HTTP错误代码
  • 原文地址:https://www.cnblogs.com/yerikm/p/15345676.html
Copyright © 2020-2023  润新知