Java并发笔记——单例与双重检测
单例模式可以使得一个类只有一个对象实例,能够减少频繁创建对象的时间和空间开销。单线程模式下一个典型的单例模式代码如下:
①
class Singleton{
private static Singleton singleton;
private Singleton(){} public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton(); //1
}
return singleton;
}
}
构造器私有使得外界无法通过构造器实例化Singleton类,要取得实例只能通过getInstance()方法。这是一个延迟加载的版本,即在需要对象的时候才进行实例化操作。该方法在单线程下能够正常运行,但是在多线程环境下会出现由于没有同步措施而导致产生多个单例对象的情况。原因在于可能同时有两个线程A和B同时执行到 if 条件判断语句,A判断singleton为空准备执行//1时让出了CPU时间片,B也判断singleton为空,接着执行//1,此时创建了一个实例对象;A获取了CPU时间片后接着执行//1,也创建了实例对象,这就导致多个单例对象的情况。
解决问题的方法也很简单,使用synchronized关键字:
②
class Singleton{
private static Singleton singleton;
private Singleton(){} public static synchronized Singleton getInstance(){
if(singleton == null){
singleton = new Singleton(); //1
}
return singleton;
}
}
这样解决了多线程并发的问题,但是却带来了效率问题:我们的目的是只创建一个实例,即//1处代码只会执行一次,也正是这个地方才需要同步,后面创建了实例之后,singleton非空就会直接返回对象引用,而不用每次都在同步代码块中进行非空验证。那么可以考虑只对//1处进行同步:
③
class Singleton{
private static Singleton singleton;
private Singleton(){} public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
singleton = new Singleton(); //1
}
}
return singleton;
}
}
这样会带来与第一种一样的问题,即多个线程同时执行到条件判断语句时,会创建多个实例。问题在于当一个线程创建一个实例之后,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(); //1
}
}
return singleton;
}
}
到这里已经很完美了,看起来没有问题。但是这种双重检测机制在JDK1.5之前是有问题的,问题还是出在//1,由所谓的无序写入造成的。一般来讲,当初始化一个对象的时候,会经历内存分配、初始化、返回对象在堆上的引用等一系列操作,这种方式产生的对象是一个完整的对象,可以正常使用。但是JAVA的无序写入可能会造成顺序的颠倒,即内存分配、返回对象引用、初始化的顺序,这种情况下对应到//1就是singleton已经不是null,而是指向了堆上的一个对象,但是该对象却还没有完成初始化动作。当后续的线程发现singleton不是null而直接使用的时候,就会出现意料之外的问题。
JDK1.5之后,可以使用volatile关键字修饰变量来解决无序写入产生的问题,因为volatile关键字的一个重要作用是禁止指令重排序,即保证不会出现内存分配、返回对象引用、初始化这样的顺序,从而使得双重检测真正发挥作用。
当然,也可以选择不使用双重检测,而采用非延迟加载的方式来达到相同的效果:
class Singleton{
private static Singleton singleton = new Singleton();
private Singleton(){} public static Singleton getInstance(){
return singleton;
}
}
【参考】
Java并发笔记——单例与双重检测的更多相关文章
- Java学习笔记——单例设计模式Singleton
单例设计模式:singleton 解决的问题: 确保程序在运行过程中,某个类的实例instance只有一份. 特点: 1 构造函数私有化 2 自己内部声明自己 3 提供一个public方法,负责实例化 ...
- Java并发-懒汉式单例设计模式加volatile的原因
懒汉式单例的double check.例一: class SingletonClass{ private static SingletonClass instance = null; private ...
- 转载:java基础之单例
转载:https://blog.csdn.net/goodlixueyong/article/details/51935526 https://www.cnblogs.com/cielosun/p/6 ...
- Java设计模式之单例
一.Java中的单例: 特点: ① 单例类只有一个实例 ② 单例类必须自己创建自己唯一实例 ③ 单例类必须给所有其他对象提供这一实例 二.两种模式: ①懒汉式单例<线程不安全> 在类加载时 ...
- Java复习11. 单例编程
Java复习11. 单例编程 1.最简单的写法,那个方式是线程不安全的 public class Singleton { private static Singleton instance; ...
- java设计模式--解决单例设计模式中懒汉式线程安全问题
首先写个单例,懒汉模式: public class SingleDemo { private static SingleDemo s = null; private SingleDemo(){} pu ...
- java并发笔记之证明 synchronized锁 是否真实存在
警告⚠️:本文耗时很长,先做好心理准备 证明:偏向锁.轻量级锁.重量级锁真实存在 由[java并发笔记之java线程模型]链接: https://www.cnblogs.com/yuhangwang/ ...
- java并发笔记之四synchronized 锁的膨胀过程(锁的升级过程)深入剖析
警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. 本篇我们讲通过大量实例代码及hotspot源码分析偏向锁(批量重偏向.批量撤销).轻量级锁.重量级锁及锁的膨胀过程(也就是锁的升 ...
- Java设计模式之单例设计模式总结
package singleton; /**单例设计模式 饿汉式 * * @author gx *这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化, ...
随机推荐
- Codeforces Round #574 (Div. 2)——C. Basketball Exercise(简单DP)
题目传送门 题意: 输入n,给出两组均为 n个数字的数组a和b,轮流从a和b数组中取出一个数字,要求严格按照当前所选数字的数组下标比上一个所选数字的数组下标更大,计算能够取出的数字加起来的总和最大能为 ...
- collection介绍
1.collection介绍 在mongodb中,collection相当于关系型数据库的表,但并不需提前创建,更不需要预先定义字段 db.collect1.save({username:'mayj' ...
- [实践]redhat linux5.3安装tomcat
1.安装准备 操作系统:RedHat 5 (自带apache2.2.3) 安装tomcat前首先要安装jdk: 查看系统是否安装了jdk或tomcat的命令: rpm -qa | grep java ...
- android ——活动
活动(Activity)主要用于和用户进行交互,是一种可以包含用户界面的组件. 1.手动创建活动 右击com.example.administrator.exp5→New→Activity→Empty ...
- Java学习|Exception和Error有什么区别?
典型回答: Exception和Error都继承了Throwable类,java中只有Throwable类型的实例才能被Throw(抛出)或者catch(捕获). Exceptio ...
- 在Win10下,python3和python2同时安装并解决pip共存问题
前提 本文是在Windows64位系统下进行的,32位系统请下载相应版本的安装包,安装方法类似. 在Win10下,python3和python2同时安装并解决pip共存问题解决: 1.下载python ...
- Python模块之ncclient
一.简介 此模块是是netconf协议的客户端,可与netconf服务端进行交互 二.实验环境 1.操作系统:win10 2.python版本:python3.6.6 3.ncclient模块版本:0 ...
- copy and mutableCopy
结论: 1, 深复制与浅复制 2,immutable和mutable 3,代码分析: #pragma mark - String - (void)stringCopyAndMutableCopy { ...
- 数据读写API——IO流
理清一些概念 1.Java 中的IO是干啥的? IO指的是Input和Output,主要目的是实现数据在存储介质之间的传输.[流:数据流,类比与水流的流动] 2.IO分类 按照操作单元来划分,可以分为 ...
- poli-java开源BI软件
目录 快速入门 Github地址: 特性 一个易于使用的SQL报告应用程序,专为SQL爱好者而设计. SQL中的电源数据分析,可获得更快的业务洞察力. 快速入门 https://shzlw.githu ...