Java双重校验单例模式详解
单例模式双重检测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双重校验单例模式详解的更多相关文章
- Java设计模式之单例模式详解
在Java开发过程中,很多场景下都会碰到或要用到单例模式,在设计模式里也是经常作为指导学习的热门模式之一,相信每位开发同事都用到过.我们总是沿着前辈的足迹去做设定好的思路,往往没去探究为何这么做,所以 ...
- 9种Java单例模式详解(推荐)
单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象. 懒汉式(线程不安全) 其主要表现在单例类在外 ...
- java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock
原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...
- Java 8 Stream API详解--转
原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...
- java反射机制深入详解
java反射机制深入详解 转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...
- 国际化,java.util.ResourceBundle使用详解
java.util.ResourceBundle使用详解 一.认识国际化资源文件 这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以: 轻松地本地化或翻译成不同的 ...
- java之StringBuffer类详解
StringBuffer 线程安全的可变字符序列. StringBuffer源码分析(JDK1.6): public final class StringBuffer extends Abstract ...
- java.util.ResourceBundle使用详解
java.util.ResourceBundle使用详解 一.认识国际化资源文件 这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以: 轻松地本地化或翻译成不同的 ...
- java之AbstractStringBuilder类详解
目录 AbstractStringBuilder类 字段 构造器 方法 public abstract String toString() 扩充容量 void expandCapacity(in ...
随机推荐
- 小白必看:零基础安装Linux系统(超级详细)
我们以最新发布的CentOS 8.1为例,学习下如何安装Linux系统 准备工作: 1.一台可以访问互联网的电脑 2.VMware Workstation安装包 3.CentOS8.1镜像(CentO ...
- Linux vs Unix - Linux与Unix到底有什么不同?
来自:Linux迷链接:https://www.linuxmi.com/linux-vs-unix.html Linux和Unix这两个术语可以互换地用来指同一操作系统.这在很大程度上是由于他们惊人的 ...
- PHP代码审计之SQL注入
代码审计之SQL注入 SQL注入攻击(SQLInjection),是攻击者在表单中提交精心构造的sql语句,改变原来的sql语句,如果web程序没有对提交的数据经过检查,那么就会造成sql注入攻击. ...
- 这 BUG,绝了
上周只上了三天班,但我也丝毫不敢懈怠,BUG 更是一个也没少写. 看着满屏幕的 ERROR,我陷入沉思.为什么我写的代如此烂,无法像大牛们写的那般优雅? 越想越自卑,越想越抑郁.我觉得这样不行,一定得 ...
- Java学习笔记-基础语法Ⅵ-异常
异常 对于异常,JVM默认处理方案为:把异常名称.异常原因以及异常出现的位置等信息输出在控制台,并且程序停止执行 异常处理方式一:try ... catch public class Demo { p ...
- Kafka 生产者解析
一.消息发送 1.1 数据生产流程 数据生产流程图解: Producer创建时,会创建⼀个Sender线程并设置为守护线程 ⽣产消息时,内部其实是异步流程:⽣产的消息先经过拦截器->序列化器-& ...
- linux篇-linux 下建立多个tomcat
第一步:复制,解压 将准备好的tomcat压缩包复制到你准备安装的目录,我的tomcat压缩包名字是tomcat.tar.gz,我的安 装目录是 /usr/java/tomcat 第二步:解压tomc ...
- Eureka高可用集群搭建
就是搭建Eureka的集群. 每个Eureka Server需要相互注册,确保数据一致. 我这里准备两个Eureka Server 他两的POM文件配置是一样的 <dependencies&g ...
- GitHub 官方大动作频频「GitHub 热点速览 v.22.24」
作者:HelloGitHub-小鱼干 本周 GitHub 官方 Blog 很是热闹,GitHub 官方大动作频频也带来了 GitHub Blog 的频繁更新,除了本周 News 快读收录的 GitHu ...
- Camunda如何配置和使用mysql数据库
Camunda默认使用已预先配置好的H2数据库,数据库模式和所有必需的表将在引擎第一次启动时自动创建.如果你想使用自定义独立数据库,比如mysql,请遵循以下步骤: 一.新建mysql数据库 为Cam ...