Java设计模式---单例模式
一、饿汉模式
/**
* @ClassName: Singleton_Simple
* @Description: 单例模式——饿汉模式
* @author Ran
* @date 2011-2-4 上午12:46:15
*
*/
public class Singleton_Simple {
private static final Singleton_Simple simple = new Singleton_Simple();
private Singleton_Simple(){}
public static Singleton_Simple getInstance(){
return simple;
}
}
说明:顾名思义,饿汉模式就是在jvm进程启动并在我们主动使用该类的时候就会在内存中初始化一个单例对象,当我们调用getInstance()的时候直接获取该对象,他的创建是在我们调用getInstance()静态方法之前!
二、懒汉模式
/**
* @ClassName: Singleton_lazy
* @Description: 单例模式——懒汉模式
* @author Ran
* @date 2011-2-4 上午12:48:41
*
*/
public class Singleton_lazy {
private static Singleton_lazy lazy = null;
private Singleton_lazy(){}
public static synchronized Singleton_lazy getInstance(){
if( lazy == null ){
lazy = new Singleton_lazy();
}
return lazy;
}
}
说明:懒汉模式是相对于饿汉模式而言的,在jvm进程启动并在我们主动使用该类的时候不会在内存中初始化一个单例对象,只有当我们调用getInstance()的时候才去创建该对象,他的创建是在我们调用getInstance()静态方法之后,为了并没现象同步问题,我们在getInstance()方法上加了一个锁,这个方法每次只允许一个线程进来,虽然同步问题是解决了,但是相应的性能问题就出现了。
三、双锁机制
/**
* @ClassName: Singleton_DoubleKey
* @Description: 单例模式——双锁机制
* @author Ran
* @date 2011-2-4 上午12:53:50
*
*/
public class Singleton_DoubleKey {
private static Singleton_DoubleKey doubleKey = null;
private Singleton_DoubleKey (){}
public static Singleton_DoubleKey getInstance(){
if( doubleKey == null ){ //①
synchronized(Singleton_DoubleKey.class){ //②
if( doubleKey == null ){ //③
doubleKey = new Singleton_DoubleKey(); //④
}
}
}
return doubleKey;
}
}
说明:双锁机制的出现是为了解决前面同步问题和性能问题,看上面的代码,简单分析下确实是解决了多线程并行进来不会出现重复new对象,而且也实现了懒加载,但是当我们静下来并结合java虚拟机的类加载过程我们就会发现问题出来了,对于JVM加载类过程不熟悉的,这里我简单介绍下,熟悉的跳过这段(当然,既然你熟悉就自然会知道双锁的弊端了)。
jvm加载一个类大体分为三个步骤:
- 加载阶段:就是在硬盘上寻找java文件对应的class文件,并将class文件中的二进制数据加载到内存中,将其放在运行期数据区的方法区中去,然后在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构;
- 连接阶段:这个阶段分为三个步骤,步骤一:验证,验证什么呢?当然是验证这个class文件里面的二进制数据是否符合java规范咯;步骤二:准备,为该类的静态变量分配内存空间,并将变量赋一个默认值,比如int的默认值为0;步骤三:解析,这个阶段就不好解释了,将符号引用转化为直接引用,涉及到指针,这里不做多的解释;
- 初始化阶段:当我们主动调用该类的时候,将该类的变量赋于正确的值(这里不要和第二阶段的准备混淆了),举个例子说明下两个区别,比如一个类里有private static int i = 5; 这个静态变量在"准备"阶段会被分配一个内存空间并且被赋予一个默认值0,当道到初始化阶段的时候会将这个变量赋予正确的值即5,了解了吧!
好了,上面大体介绍了jvm类加载过程,回到我们的双锁机制上来分析下问题出在了哪里?假如有两个并发线程a、b,a线程主动调用了静态方法getInstance(),这时开始加载和初始化该类的静态变量,b线程调用getInstance()并等待获得同步锁,当a线程初始化对象过程中,到了第二阶段即连接阶段的准备步骤时,静态变量doubleKey 被赋予了一个默认值,但是这时还没有进行初始化,这时当a线程释放锁后,b线程判断doubleKey != null,则直接返回了一个没有初始化的doubleKey 对象,问题就出现在这里了,b线程拿到的是一个被赋予了默认值但是未初始化的对象,刚刚可以通过锁的检索!
所以对于上面的几个模式还是推荐使用第一种,在jvm加载类的时候就初始化一个对象,也避免了同步问题。
分享到:
相关推荐
行业文档-设计装置-双锁带钢筋卡扣.zip
Singleton模式可以相当的复杂,比头五种模式加起来还复杂,譬如涉及到DCL双锁检测(double checked locking)的讨论、涉及到多个类加载器(ClassLoader)协同时、涉及到跨JVM(集群、远程EJB等)时、涉及到单例对象...
电子政务-双锁紧的电缆防水接头.zip
行业资料-交通装置-一种双锁双控公共自行车锁车器.zip
行业资料-电子功用-升降双锁止吊钩式转动型变电所机电设备安装机构的介绍分析.rar
行业资料-电子功用-升降双锁止斗篮式转动型变电所机电设备安装机构的介绍分析.rar
一种基于虚拟仪器技术的双锁相放大器的设计
电子政务-可电子遥控的双锁舌防盗锁.zip
行业资料-电子功用-支腿可调节、升降双锁止、斗篮式转动型变电所机电设备安装机构
电子政务-一种输电线路直线单元壳体双锁紧检测装置.zip
电子政务-一种输电线路直线单元壳体双锁紧检测工装.zip
行业资料-电子功用-支腿可调节斗篮式升降双锁止变电所机电设备安装机构
重19-马拉松运动员训练特点分析以运动员李双锁和汪红祥为例
重19-马拉松运动员训练特点分析以运动员李双锁和汪红祥为例
介绍了一种采用双锁相放大器实现的弱信号检测设计与实现
在此线程模式下,应用层可以在回调函数里处理数据而不必再建立另外的数据处理线程池。 2、线程平衡 为使连接能平衡使用处理线程,每个连接同时只有一个处理线程处理工作线程投递过来的IO返回事件并调用回调函数...
锈锁错误检测器静态检测MIR上的双锁和冲突锁错误。 这项工作是对我们详细研究Rust的后续工作,该研究在PLDI'20中中的问题。 我很荣幸能与Chen Yilun Chen共同发表第一作者,并且能够与具有远见,知识渊博,勤奋工作...
MB15F72是双锁相环芯片,文件夹内含mb15f72.c和mb15f72.h
GP214D是一款双锁相环芯片,文件夹内包括gp214d.c和gp214d.h
为使连接能平衡使用IO设备,每个连接同时只能投递一个读请求,并通过线程平衡机制保证接收的数据是按顺序的被处理线程处理及通过回调函数传递给应用层;同时也只能投递一个写请求,其余写请求都按顺序放在写队列...