【设计模式】单例设计模式的N中Java实现方法
2015-05-17 17:35
363 查看
特点
单例模式的特点:1、仅仅能有一个实例;
2、必须自己创建自己的一个实例;
3、必须给全部其它对象提供这一实例。
饿汉式单例模式
也称为预先载入法,实现方式例如以下:[java] view
plaincopy
class Single
{
private Single()(
Syustem.out.println("ok");
)
private static Single instance = new Single();
public static Single getInstance(){
return instance;
}
}
长处:线程安全,调用时反应速度快,在类载入的同一时候已经创建好了一个静态对象(创建的唯一对象);
缺点:资源利用效率不高,可能该实例并不须要,但也被系统载入了。另外,饿汉式在一些场景下是无法使用的,比方,假设Single实例的创建依赖參数或配置文件,则在getInstance()之前必须调用某个方法来设置这些參数,但在设置之前,可能已经new了Single实例,这样的情况下,饿汉式的写法是无法使用的。
懒汉式单例模式
也称为延迟载入法,实现方式例如以下:[java] view
plaincopy
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
延迟载入法在适用于单线程环境,它不是线程安全的,引入多线程时,就必须通过同步来保护getInstance()方法,否则可能会返回LazySingleton的两个不同实例。比方,一个线程在推断instance为null后,还没来得及创建新的instance,还有一个线程此时也推断到instance为null,这样两个线程便会创建两个LazySingleton实例。
能够将getInstance()方法改为同步方法,这样便能够避免上述问题,改进后的单例模式实现例如以下:
[java] view
plaincopy
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
长处:资源利用率高,不运行getInstance就不会被实例。
缺点:第一次载入时反应不快,多线程使用不必要的同步开销大
这里的缺点主要是:每次调用getInstance()方法时,都要同步,并且非常多时候的同步是不是必需的,这将会极大地拖垮性能(尤其在须要多次调用getInstance方法的地方,当第一次创建了LazySingleton实例后,instance便不再为null,这样后面每次调用getInstance进入方法体后,却便发现自己什么也不用做,而每次调用getInstnce都要同步,须要切换到内核,这样便非常浪费资源,每次做非常大开销进入方法体,却发现自己什么也不用做)。
DCL单例模式
针对延迟载入法的同步实现所产生的性能低的问题,我们能够採用DCL,即双重检查加锁(Double Check Lock)的方法来避免每次调用getInstance()方法时都同步。实现方式例如以下:[java] view
plaincopy
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized(LazySingleton.class){
if(instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
长处:资源利用率高,不运行getInstance就不会被实例,多线程下效率高。
缺点:第一次载入时反应不快,因为java 内存模型一些原因偶尔会失败,在高并发环境下也有一定的缺陷,尽管发生概率非常小。
DCL对instance进行了两次null推断,第一层推断主要是为了避免不必要的同步,第二层的推断则是为了在null的情况下创建实例。
对于DCL的不安全性,我们来看看例如以下场景:
如果线程A运行到instance = new LazySingleton()这句,这里看起来是一句话,但实际上它并非一个原子操作,我们仅仅要看看这句话被编译后在JVM运行的相应汇编代码就发现,这句话被编译成8条汇编指令,大致做了3件事情:
1.给LazySingleton的实例分配内存。
2.初始化LazySingleton()的构造器
3.将instance对象指向分配的内存空间(注意到这步instance就非null了)。
可是,由于Java编译器同意处理器乱序运行,以及JDK1.5之前JMM(Java Memory Medel,即Java内存模型)中Cache、寄存器到主内存回写顺序的规定,上面的第二点和第三点的顺序是无法保证的,也就是说,运行顺序可能是1-2-3也可能是1-3-2,假设是后者,并且在3运行完成、2未运行之前,被切换到线程B上,这时候instance由于已经在线程A内运行过了第三点,instance已经是非空了,所以线程B直接拿走instance,然后使用,然后顺理成章地报错,并且这样的难以跟踪难以重现的错误非常可能会隐藏非常久。
DCL的写法来实现单例是非常多技术书、教科书(包含基于JDK1.4曾经版本号的书籍)上推荐的写法,实际上是不全然正确的。的确在一些语言(譬如C语言)上DCL是可行的,但这取决于能否保证2、3步的顺序。在JDK1.5之后,官方已经注意到这样的问题,调整了JMM、详细化了volatilekeyword,因此假设JDK是1.5或之后的版本号,仅仅须要将instance的定义改成“private volatile
static LazySingleton instance = null;”就能够保证每次都去instance都从主内存读取,就能够使用DCL的写法来完毕单例模式。当然volatile或多或少也会影响到性能,最重要的是我们还要考虑JDK1.4以及之前的版本号,所以本文中单例模式写法的改进还在继续。
static内部类单例模式
该方法是为了解决DCL方法在并发环境中的缺陷而提出的,关于DCL在并发编程中存在的问题能够參考这篇文章:/article/1339993.html的后半部分,事实上现方式例如以下:[java] view
plaincopy
class Single
{
private Single()(
Syustem.out.println("ok");
)
private static class InstanceHolder{
private static final Singlet instance = new Single();
}
public static Single getInstance(){
return InstanceHolder.instance;
}
}
长处:线程安全,资源利用率高,不运行getInstance就不会被实例。
缺点:第一次载入时反应不快。
这里针对最后一种方法补充下面基本知识点:类级内部类(有static修饰的成员内部类)相当于其外部类的成员,仅仅有在第一次使用时才会被装载,而不会在类载入器载入其外部类的时候被装载,并且仅仅会被载入一次。因此,资源利用率高。
总结:在Java中因为会涉及到并发编程,考虑到效率、安全性等问题,一般经常使用饿汉式单例模式或static内部类单例模式,而后者又是最优且最经常使用的单例设计模式的实现方法。
相关文章推荐
- 追MM与设计模式的JAVA实现-简单工厂方法
- 设计模式之—简单工厂方法(StaticFactory )-Java实现
- JAVA设计模式——单例(Singleton)模式 多种实现方法
- 【设计模式】单例设计模式的N中Java实现方法
- 【设计模式】单例设计模式的N中Java实现方法
- 单例设计模式的N中Java实现方法
- 设计模式:浅析 抽象工厂、工厂方法、简单(静态)工厂 java实现
- Java设计模式菜鸟系列(十)模板方法模式建模与实现
- 设计模式之第3章-模板方法模式(Java实现)
- 设计模式之模板方法模式(Java实现)
- 【设计模式】单例设计模式的N中Java实现方法
- 使用模板方法模式实现可扩展性设计(Java示例)
- 设计模式:浅析 抽象工厂、工厂方法、简单(静态)工厂 java实现
- 设计模式(Design Pattern) - 行为型模式(Behavioral Pattern) - 模板方法模式(Template Method) - Java实现
- 设计模式--模板方法模式--Java实现-- java访问控制关键字用法
- Java设计模式之工厂模式实现方法详解
- Rhyme/Java 设计模式之原型模式prototype通过clone方法和反序列化实现深复制
- Java设计模式中适配器模式的实现方法
- [置顶] 【JAVA】设计模式之懒汉式与恶汉式的单例模式实现的方法与详解
- 【JAVA】设计模式之懒汉式与恶汉式的单例模式实现的方法与详解