单例模式双重检测java实现:

public class Singleton {
private volatile static Singleton instance = null; //#1
public static Singleton getInstance() {
if (instance == null) { //#2
synchronized (SingletonClass.class) { //#3
if(instance == null) { //#4
instance = new Singleton();
}
}
}
return instance;
}

1.1:为什么要使用volatile?

通常来说在堆中创建变量,会有两个步骤:

在堆中分配内存空间、执行初始化(就是new做得事情)

在栈中的本地变量表分配一个指向该内存区域的reference(等于号做得事情)

但JVM会进行编译优化,并不一定按照这样的顺序执行。在多线程环境下,若线程A创建instance,首先分配了reference的指针,此时线程B并发地去执行getInstance方法,那么会发现instance所指向的内存区域并不是null,那么线程B的getInstance方法则会返回这个instance,但实际上线程A仅仅是分配了这个指针,并没有在内存区域中完成初始化方法。

volatile禁止了JVM对指令顺序的优化,使得创建变量严格按照先分配内存再分配指针的顺序执行。

同时,如果没有volatile,某线程修改变量,并不会立即存入共享内存(主存),而是存入线程私有的一块高速缓存区域(在CPU中的cache),而volatile要求线程对变量修改完之后立即存入共享内存,保证了变量修改的可见性。

1.2:为什么要static?

保证单例对象是属于类的,而不是属于对象实例的,保证一个类只有一个变量。

3 为什么不给getInstance方法修饰synchronized,而是在这里给类上锁?

因为没有必要,如果在最外层判断出已有单例对象,则无需调用任何同步方法。而若给getInstance方法修饰synchronized,那么无论如何都有synchronized带来的额外开销(即便synchronize进行了大量优化)。

4 为什么需要第二层检验?

在多线程环境下,若没有语句#4,想象这样的场景:

线程A执行完#2

在#2、#3之间发生了线程切换,切换到线程B

线程B执行#3,获取到了锁,并进行instance初始化

切换回线程A,线程A执行#3,获取到了锁,并进行instance初始化

会发现instance被初始化了两次,因此必须进行第二层检验。

Java双重校验单例模式详解的更多相关文章

  1. Java设计模式之单例模式详解

    在Java开发过程中,很多场景下都会碰到或要用到单例模式,在设计模式里也是经常作为指导学习的热门模式之一,相信每位开发同事都用到过.我们总是沿着前辈的足迹去做设定好的思路,往往没去探究为何这么做,所以 ...

  2. 9种Java单例模式详解(推荐)

    单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外 ...

  3. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  4. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...

  5. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

  6. 国际化,java.util.ResourceBundle使用详解

    java.util.ResourceBundle使用详解   一.认识国际化资源文件   这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以:          轻松地本地化或翻译成不同的 ...

  7. java之StringBuffer类详解

    StringBuffer 线程安全的可变字符序列. StringBuffer源码分析(JDK1.6): public final class StringBuffer extends Abstract ...

  8. java.util.ResourceBundle使用详解

    java.util.ResourceBundle使用详解   一.认识国际化资源文件   这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以:          轻松地本地化或翻译成不同的 ...

  9. java之AbstractStringBuilder类详解

    目录 AbstractStringBuilder类 字段 构造器 方法   public abstract String toString() 扩充容量 void  expandCapacity(in ...

随机推荐

  1. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

  2. 520,用Python定制你的《本草纲目女孩》

    摘要:让我们来用Python定制出心仪的"本草纲目女孩",敲出魔性的代码舞蹈,520,准备好心仪女孩的舞蹈视频,把这份别出心裁的礼物给TA 本文分享自华为云社区<[云驻共创] ...

  3. 每天一个 HTTP 状态码 202

    202 Accepted 202 Accepted 表示服务器已经接受了这个请求,但是还不确定这个请求是否能够成功地被处理完.该请求最终可能会或可能不会被执行,并且在处理发生时可能会被拒绝,这是不确定 ...

  4. babel使用

    Babel转码器 Babel定义 Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在老版本的浏览器执行 Babel安装 仅需要在项目文件下安装 npm ins ...

  5. 这篇 DNS ,写的挺水的。

    试想一个问题,我们人类可以有多少种识别自己的方式?可以通过身份证来识别,可以通过社保卡号来识别,也可以通过驾驶证来识别,尽管有多种识别方式,但在特定的环境下,某种识别方法会比其他方法更为适合.因特网上 ...

  6. 题解 CF1095F 【Make It Connected】

    题意简述 \(n\)( \(1≤n≤2×10^5\) )个点,每个点 \(i\) 有一个点权 \(a_i\) ( \(1≤a_i≤2×10^{12}\) ),将两个点 \(i\),\(j\) 直接相连 ...

  7. 揭秘华为云GaussDB(for Influx)最佳实践:hint查询

    摘要:GaussDB(for Influx)通过提供hint功能,在单时间线的查询场景下,性能有大幅度的提升,能有效满足客户某些特定场景的查询需求. 本文分享自华为云社区<华为云GaussDB( ...

  8. Tomcat启动失败:java.lang.NoSuchMethodError: org.apache.tomcat.util.res.StringManager.getManager(Ljava/lang/Class;)Lorg/apache/tomcat/util/res/StringManager

    项目开发中发现服务器上Tomcat启动失败 开始定位 第一步:打开tomcat日志catalina.log: 2017-07-25 17:02:43,799 [Catalina-startStop-1 ...

  9. Blazor WebAssembly + Grpc Web = 未来?

    Blazor WebAssembly是什么 首先来说说WebAssembly是什么,WebAssembly是一个可以使C#,Java,Golang等静态强类型编程语言,运行在浏览器中的标准,浏览器厂商 ...

  10. Linux/Ubuntu 安装Redis

    更新记录 2022年6月15日 发布. 2022年6月12日 开始编写. 安装Redis 更新源 sudo apt update 安装redis sudo apt install redis-serv ...