ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

双重检查锁模式的应用

2021-11-21 11:30:00  阅读:204  来源: 互联网

标签:初始化 检查 双重 模式 static 指令 volatile 线程 排序


双重检查锁模式的应用


双重检查锁定模式,关键要素 volatilesynchronized 除了用于防止接口被重复调用以外,还可以用于创建单例对象。
1应用场景及思想

  • 单例模式:首先判断变量是否被初始化,没有被初始化,再去获取锁。获取锁之后,再次判断变量是否被初始化。 第二次判断目的在于有可能其他线程获取过锁,已经初始化改变量。第二次检查还未通过,才会真正初始化变量。
  • 防止接口重复调用:适用于花费时间比较长的接口,先定义一个volatile修饰的初始值为falseboolean变量作为标志位,首先判断这个标志位是否为false,发现这个接口没有被调用后再去获取线程同步锁,获取到锁之后再次判断该标志位是否为false,第二次检查该接口还未被调用就开始执行真正的调用逻辑

2优点

  • 比单纯的仅仅使用线程同步锁synchronized,缩小锁了的范围,减少了锁的开销
  • 为什么要用 volatile 来修饰变量呢?主要是为了解决指令重排问题,而volatile主要包含两个功能。
    1)、是保证可见性:使用 volatile定义的变量,将会保证对所有线程的可见性。
    2)、禁止指令重排序优化。
    因此由于 volatile禁止对象创建时指令之间的重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。
    注意:那么指令重排有什么危害呢?首先指令重排主要针对创建对象的场景,众所周知,new(创建对象)并不是一个原子指令,使用 javap -c指令可以看到
  • 创建一个对象实例,可以分为三步:
      1、分配对象内存
      2、调用构造器方法,执行初始化
      3、将对象引用赋值给变量。

虚拟机实际运行时,以上指令可能发生重排序。也就是说以上方法执行的第 2,3 步可能发生重排序,但是并不会重排序 1 的顺序。也就是说 1 这个指令都需要先执行因为 2,3 指令需要依托 1 指令执行结果,Java 语言规规定了线程执行程序时需要遵守 intra-thread semantics。intra-thread semantics 保证了重排序不会改变单线程内的程序执行结果。在没有改变单线程程序的执行结果的前提下,可以提高程序的执行性能。 但是,虽然重排序并不影响单线程内的执行结果,但是在多线程的环境就会带来一些问题。

就普通单例模式而言,两个线程,如果线程 1 获取到锁进入创建对象实例的时候发生了指令重排序。先将对象引用赋值给变量,然后当线程1 即将准备执行第三步的时候,线程 2 刚好进入,由于此时对象已经不为 Null,所以线程 2 可以自由访问该对象。但是该对象还未初始化,所以线程 2 访问时将会发生异常。这就是指令重排所带来的影响
应用场景一:


private volatile boolean excuteFlag = false;
/**
 * 商品数据全量同步
 *	防止接口重复调用:
 * 双重检查锁定模式,关键要素 volatile synchronized 还可以用于创建单例
 * @return
 */
@GetMapping("syncSpuInfo")
public R putOnSale(){
    if(!excuteFlag){
        synchronized (this){
            if(!excuteFlag){
                excuteFlag = true;
                 return spuInfoService.syncSpuInfo();
            }
        }
    }
    return R.ok("数据正在导入中,请勿重复执行");
}

应用场景二:
1)、双重检查锁

private volatile static  Cache  cache;
	public static Cache getCache(){
		if(cache == null){
			synchronized (this){
				if(cache == null){
					cache = new Cache();
				}
			}
		}
	return cache;
	}

2)、静态内部类

public class Singleton {
    private Singleton() {}
    
    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

标签:初始化,检查,双重,模式,static,指令,volatile,线程,排序
来源: https://blog.csdn.net/qq_45036013/article/details/121450918

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有