精华内容
下载资源
问答
  • volatile与synchronized的区别,锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)
  • 2.volatile 与synchronized区别: 关键字 volatile 是线程同步轻量级实现,性能比 synchronized 要好,并且 volatile 只能修于变量,而 synchronized 可以修饰方法,代码块等。 多线程访问 v...

    1.volatile 关键字的作用就是强制从公共堆栈中取得变量的值,而不是线程私有的数据栈中取得变量的值。

    如下图所示:

     

    2.volatile 与 synchronized区别:

    1. 关键字 volatile 是线程同步的轻量级实现,性能比 synchronized 要好,并且 volatile 只能修于变量,而 synchronized 可以修饰方法,代码块等
    2. 多线程访问 volatile 不会发生阻塞,而 synchronized 会发生阻塞
    3. volatile 可以保证数据的可见性,但不可以保证原子性,而 synchronized 可以保证原子性,也可以间接保证可见性,因为他会将私有内存和公共内存中的数据做同步。
    4. volatile 解决的是变量在多个线程之间的可见性,而 synchronized 解决的是多个线程之间访问资源的同步性。

     

    3.volatile 的使用

    Spring 中使用到volatile的简单截图,不多做解释,示意图如下:

    volatile 修饰的都是属性而不是方法,Spring 使用 volatile 来保证变量在多个线程之间的可见性

    展开全文
  • Volatile 特性: 保证了不同线程对这个变量进行操作时可见性,即一个线程修改了某个变量值,这新值对其他线程来说是立即可见。(实现可见性) 禁止进行指令重排序。(实现有序性) volatile 只能保证对单次...

    Volatile

    Volatile 的特性:

    保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)

    禁止进行指令重排序。(实现有序性)

    volatile 只能保证对单次读/写的原子性,i++ 这种操作不能保证原子性

    Volatile可见性
    当写一个volatile变量时,JMM会把该线程对应的工作内存中的共享变量值更新后刷新到主内存,

    当读取一个volatile变量时,JMM会把该线程对应的工作内存置为无效,线程会从主内存中读取共享变量。

    写操作:
    在这里插入图片描述
    读操作:
    在这里插入图片描述
    Volatile 禁止指令重排
    JMM对volatile的禁止指令重排采用内存屏障插入策略:

    在每个volatile写操作的前面插入一个StoreStore屏障。在每个volatile写操作的后面插入一个StoreLoad屏障
    在这里插入图片描述
    在每个volatile读操作的后面插入一个LoadLoad屏障。在每个volatile读操作的后面插入一个LoadStore屏障
    在这里插入图片描述

    Synchronized

    Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:

    原子性:确保线程互斥的访问同步代码;

    可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的

    有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”;

    Synchronized总共有三种用法:

    当synchronized作用在实例方法时,监视器锁(monitor)便是对象实例(this);

    当synchronized作用在静态方法时,监视器锁(monitor)便是对象的Class实例,因为Class数据存在于永久代,因此静态方法锁相当于该类的一个全局锁;

    当synchronized作用在某一个对象实例时,监视器锁(monitor)便是括号括起来的对象实例;

    理解了Volatile与Synchronized后,那我们来看看如何使用Volatile与Synchronized优化单例模式
    单例模式优化-双重检测DCL(Double Check Lock)
    先来看看一般模式的单例模式:

    class Singleton{
       private static Singleton singleton;    
       private Singleton(){}
    
       public static Singleton getInstance(){
               if(singleton == null){
                   singleton = new Singleton();   // 创建实例
          }
           return singleton;
      }
    }
    
    

    可能出现问题:当有两个线程A和B,

    线程A判断if(singleton == null)准备执行创建实例时,线程挂起,

    此时线程B也会判断singleton为空,接着执行创建实例对象返回;

    最后,由于线程A已进入也会创建了实例对象,这就导致多个单例对象的情况

    首先想到是那就在使用synchronized作用在静态方法:

    
    public class Singleton {
       private static Singleton singleton;
       private Singleton(){}
       public static synchronized Singleton getInstance(){
           if(singleton == null){
          singleton = new Singleton();
          }
           return singleton;
      }
    }
    

    虽然这样简单粗暴解决,但会导致这个方法比较效率低效,导致程序性能严重下降,那是不是还有其他更优的解决方案呢?

    可以进一步优化创建了实例之后,线程再同步锁之前检验singleton非空就会直接返回对象引用,而不用每次都在同步代码块中进行非空验证,

    如果只有synchronized前加一个singleton非空,就会出现第一种情况多个线程同时执行到条件判断语句时,会创建多个实例

    因此需要在synchronized后加一个singleton非空,就不会出现会创建多个实例,

    
    class Singleton{
       private static Singleton singleton;    
       private Singleton(){}
       
       public static Singleton getInstance(){
           if(singleton == null){
               synchronized(Singleton.class){
                   if(singleton == null)
                       singleton = new Singleton();   
              }
          }
           return singleton;
      }
    }
    

    这个优化方案虽然解决了只创建单个实例,由于存在着指令重排,会导致在多线程下也是不安全的(当发生了重排后,后续的线程发现singleton不是null而直接使用的时候,就会出现意料之外的问题。)。导致原因singleton = new Singleton()新建对象会经历三个步骤:

    1.内存分配

    2.初始化

    3.返回对象引用

    由于重排序的缘故,步骤2、3可能会发生重排序,其过程如下:

    1.分配内存空间

    2.将内存空间的地址赋值给对应的引用

    3.初始化对象

    那么问题找到了,那怎么去解决呢?那就禁止不允许初始化阶段步骤2 、3发生重排序,刚好Volatile 禁止指令重排,从而使得双重检测真正发挥作用。

    public class Singleton {
       //通过volatile关键字来确保安全
       private volatile static Singleton singleton;
       private Singleton(){}
       public static Singleton getInstance(){
           if(singleton == null){
              synchronized (Singleton.class){
                   if(singleton == null){
                   singleton = new Singleton();
              }
          }
      }
       return singleton;
      }
    }
    

    最终我们这个完美的双重检测单例模式出来了

    总结
    volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

    volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的

    volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性

    volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

    volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

    使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 509
精华内容 203
关键字:

volatile与synchronized的区别