Java成长之路

面试:为了进阿里,重新翻阅了Volatile与Synchroni

2020-09-07  本文已影响0人  Java古德

在深入理解使用Volatile与Synchronized时,应该先理解明白Java内存模型 (Java Memory Model,JMM)

Java内存模型(Java Memory Model,JMM)

Java内存(JMM)模型是在硬件内存模型基础上更高层的抽象,它屏蔽了各种硬件和操作系统对内存访问的差异性,从而实现让Java程序在各种平台下都能达到一致的并发效果。

JMM的内部工作机制

JMM内部会有指令重排,并且会有af-if-serial跟happen-before的理念来保证指令的正确性

Java内存模型为了解决多线程环境下共享变量的一致性问题,包含三大特性,

在理解了JMM的时,来讲讲Volatile与Synchronized的使用,Volatile与Synchronized到底有什么作用呢?

Volatile

Volatile 的特性:

Volatile可见性

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

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

写操作:

读操作:

Volatile 禁止指令重排

JMM对volatile的禁止指令重排采用内存屏障插入策略:

在每个volatile写操作的前面插入一个StoreStore屏障。在每个volatile写操作的后面插入一个StoreLoad屏障

在每个volatile读操作的后面插入一个LoadLoad屏障。在每个volatile读操作的后面插入一个LoadStore屏障

Synchronized

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

Synchronized总共有三种用法:

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

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

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

理解了Volatile与Synchronized后,那我们来看看如何使用Volatile与Synchronized优化单例模式

单例模式优化-双重检测DCL(Double Check Lock)

先来看看一般模式的单例模式:

class Singleton{

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

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

public class Singleton {

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

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

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

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

class Singleton{

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

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

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

public class Singleton {

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

总结

上一篇 下一篇

猜你喜欢

热点阅读