java设计模式之单例模式写法,懒汉,饿汉,双检锁
来源:互联网 发布:深圳淘宝运营助理招聘 编辑:程序博客网 时间:2024/05/30 05:21
关键字: singleton 单例 写法 双锁 线程安全
饿汉式单例类
public class Singleton
{
private Singleton(){
}
private static Singleton instance = new Singleton();
private static Singleton getInstance(){
return instance;
}
}
饿汉式提前实例化,没有懒汉式中多线程问题,但不管我们是不是调用getInstance()都会存在一个实例在内存中
内部类式单例类
public class Singleton
{
private Singleton(){
}
private class SingletonHoledr(){
private static Singleton instance = new Singleton();
}
private static Singleton getInstance(){
return SingletonHoledr.instance;
}
}
内部类式中,实现了延迟加载,只有我们调用了getInstance(),才会创建唯一的实例到内存中.并且也解决了懒汉式中多线程的问题.解决的方式是利用了Classloader的特性.
懒汉式单例类
public class Singleton
{
private Singleton(){
}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
return instance = new Singleton();
}else{
return instance;
}
}
}
在懒汉式中,有线程A和B,当线程A运行到第8行时,跳到线程B,当B也运行到8行时,两个线程的instance都为空,这样就会生成两个实例。解决的办法是同步:
可以同步但是效率不高:
双检锁写法:
public class Singleton{
private static Singleton instance; //声明静态的单例对象的变量
private Singleton(){} //私有构造方法
public static Singleton getSingle(){ //外部通过此方法可以获取对象
if(instance== null){
synchronized (Singleton.class) { //保证了同一时间只能只能有一个对象访问此同步块
if(instance== null){
instance= new Singleton();
}
}
}
return instance; //返回创建好的对象
}
}
public class Singleton{
private static Singleton instance; //声明静态的单例对象的变量
private Singleton(){} //私有构造方法
public static Singleton getSingle(){ //外部通过此方法可以获取对象
if(instance== null){
synchronized (Singleton.class) { //保证了同一时间只能只能有一个对象访问此同步块
if(instance== null){
instance= new Singleton();
}
}
}
return instance; //返回创建好的对象
}
}
为什么要在 if 语句中使用两次判断instance== null ,这里涉及到一个名词 Double-Check Locking ,也就是双重检查锁定,为何要使用双重检查锁定呢?
考虑这样一种情况,就是有两个线程同时到达,即同时调用 GetInstance(),此时由于instance== null ,所以很明显,两个线程都可以通过第一重的instance== null ,进入第一重 if 语句后,由于存在锁机制,所以会有一个线程进入 lock 语句并进入第二重instance== null ,而另外的一个线程则会在 lock 语句的外面等待。而当第一个线程执行完 new Singleton()语句后,便会退出锁定区域,此时,第二个线程便可以进入 lock 语句块,此时,如果没有第二重instance== null 的话,那么第二个线程还是可以调用 new Singleton()语句,这样第二个线程也会创建一个 Singleton 实例,这样也还是违背了单例模式的初衷的,所以这里必须要使用双重检查锁定。
细心的朋友一定会发现,如果我去掉第一重instance== null ,程序还是可以在多线程下完好的运行的,考虑在没有第一重instance== null 的情况下,当有两个线程同时到达,此时,由于 lock 机制的存在,第一个线程会进入 lock 语句块,并且可以顺利执行 new Singleton(),当第一个线程退出 lock 语句块时,instance这个静态变量已不为 null 了,所以当第二个线程进入 lock 时,还是会被第二重instance== null 挡在外面,而无法执行 new Singleton(),所以在没有第一重instance== null 的情况下,也是可以实现单例模式的?那么为什么需要第一重instance== null 呢?这里就涉及一个性能问题了,因为对于单例模式的话,new Singleton()只需要执行一次就 OK 了,而如果没有第一重instance== null 的话,每一次有线程进入 GetInstance()时,均会执行锁定操作来实现线程同步,这是非常耗费性能的,而如果我加上第一重instance== null 的话,那么就只有在第一次,也就是instance ==null 成立时的情况下执行一次锁定以实现线程同步,而以后的话,便只要直接返回 Singleton 实例就 OK 了而根本无需再进入 lock 语句块了,这样就可以解决由线程同步带来的性能问题了。
这就是所谓的“双检锁”机制(顾名思义)。
很可惜,这样的写法在很多平台和优化编译器上是错误的。
原因在于:instance = new Singleton()这行代码在不同编译器上的行为是无法预知的。一个优化编译器可以合法地如下实现instance = new Singleton():
1. instance = 给新的实体分配内存
2. 调用Singleton的构造函数来初始化instance的成员变量
现在想象一下有线程A和B在调用getInstance,线程A先进入,在执行到步骤1的时候被踢出了cpu。然后线程B进入,B看到的是instance 已经不是null了(内存已经分配),于是它开始放心地使用instance,但这个是错误的,因为在这一时刻,instance的成员变量还都是缺省值,A还没有来得及执行步骤2来完成instance的初始化。
当然编译器也可以这样实现:
1. temp = 分配内存
2. 调用temp的构造函数
3. instance = temp
如果编译器的行为是这样的话我们似乎就没有问题了,但事实却不是那么简单,因为我们无法知道某个编译器具体是怎么做的,因为在Java的memory model里对这个问题没有定义。
双检锁对于基础类型(比如int)适用。很显然吧,因为基础类型没有调用构造函数这一步
- java设计模式之单例模式写法,懒汉,饿汉,双检锁
- Java设计模式之单例模式(懒汉/饿汉)
- java单例设计模式之懒汉模式
- 单例设计模式之懒汉模式
- 设计模式之单例模式:懒汉&饿汉
- 设计模式:java单例模式 懒汉&饿汉
- java 单例设计模式之:懒汉式、饿汉式
- 单例设计模式之懒汉式
- Java单例模式之懒汉
- 多线程-单例设计模式懒汉 饿汉
- 设计模式之单例设计模式(懒汉式)
- java 单例模式 懒汉式 饿汉式 写法实例
- 懒汉单例设计模式
- 设计模式之单例设计模式(饿汉单例设计模式&懒汉单例设计模式)
- Java中的单例设计模式之饿汉模式、懒汉模式
- Java 单例模式之饿汉模式 懒汉模式
- 单例模式懒汉试最佳写法
- Java设计模式(一):单例设计模式(singleton)之懒汉式
- Valid Parentheses
- PDU编码规则
- 个人感悟
- HDU1269(Tarjan算法)
- webmagic_dependency
- java设计模式之单例模式写法,懒汉,饿汉,双检锁
- 键-值观察(KVO模式)
- 初学者怎样才能开发出高效的J2EE系统
- 如何让表格的奇数行和偶数行具有不同的背景颜色
- 角度制与弧度制
- AlipayRsaLib.a(base64.o) ld: 2 duplicate symbols for architecture i386
- 荣耀A55高调上市只为孤独求败?
- 优化Android App性能?十大技巧必知!
- JLINK调试问题总结