【23种设计模式之一】单例设计模式(翻译)

引言:java

    这一系列文章,翻译自网络上的文章,不过中间会夹杂着我的的理解,非原创,不过中文应该算是原创。
数据库

    下面介绍,使用设计模式的一些好处:
设计模式

    一、设计模式是已经在工业生产中使用的,用于解决特定问题的标准方法,若是你在遇到相似问题的时候,能参考相应的设计,会节省你不少的时间。
api

    二、设计模式,每每能提升代码的重用性,会减小你的开发时间。
缓存

    三、既然你们都知道设计模式,包括从你的类的命名上面,类的使用上面,你们都很清楚的知道相应类的具体功能,就像每一个人都约定好的那样。
安全

 分类:
网络

    java设计模式分为3大类:
多线程

    一、对象建立
并发

    二、结构
app

    三、行为

对象建立设计模式的定义

    一、在特定的问题场景中,建立设计模式会合理的根据特定的问题建立对象。由于对象的建立可能会很复杂,会增长没必要要的复杂性,建立设计模式以不一样的建立方法解决了这些复杂性。

对象建立设计模式包含的范围:

    一、单例

    二、工厂

    三、抽象工厂

    四、建造者

    五、原型

    本节会讲一下,对象建立中的单例设计模式。

    单例设计模式,大部分人第一印象感受很简单,看完这篇博客,我想你就不会这么认为啦,不一样的开发者对于以何种方式实现单例设计模式是有争议的,本文尽量的把目前实现单例的方法归纳出来,并给出最佳实战的建议。

单例模式的定义:

    单例模式是一种建立对象实例的设计模式,在建立对象的过程当中,他会保证,在一个java虚拟机里面只有一个对象实例,同时单例设计模式,必须提供一个给外部访问该惟一对象的入口。

单例模式的应用场景:

    日志、驱动对象、缓存、线程池等等,固然设计模式不是单一存在的,其它设计模式中也会使用单例设计模式来完成一些事情,好比:抽象工厂,建造者,原型,门面等。同时在java核心包里面也有其身影,好比java.lang.runtime.

单例模式的实现方法的共性:

    一、提供私有的构造函数,防止外部类直接初始化该对象

    二、提供私有的静态变量,固然要是该类的对象实例,惟一的对象实例。

    三、public的静态方法,这就是上面说的,提供给外部类获取该类对象的惟一入口。

接下来,咱们会以不一样的方法,来实现单例设计模式。

【A】饥饿实现

    饥饿实现就是,等不急,当类被classloader加载的时候就初始化了该对象,这种实现有个弊端就是,当client不使用该类实例就浪费啦,不过我我的以为,这种状况不存在,你不用他写他作什么呢?代码测试要有覆盖率的,测试每天在你后面催着喊你把他删掉。

public class EagerInitializedSingleton 
{
     private static final EagerInitializedSingleton instance = new
     EagerInitializedSingleton();
    //private constructor to avoid client applications to use constructor
     private EagerInitializedSingleton(){}
     public static EagerInitializedSingleton getInstance()
     {
        return instance;
     }
}

    若是你的单例对象在建立的时候不使用过多的资源,这种方法是可行的。可是大部分的时候,单例对象建立的时候会加载不少的资源文件,好比File system,数据库链接等等。咱们应该避免在客户端调用getInstance方法以前建立这个对象,可是这种方法没法提供异常的捕获,异常的抛出,可能在建立对象的时候。

【B】静态块的实现方式

    静态块的实现方式和上面的实现方式差很少,可是静态块的实现,能够处理异常。代码以下:

public class StaticBlockSingleton
{
    private static StaticBlockSingleton instance;
    private StaticBlockSingleton(){}
    //static block initialization for exception handling
    static
    {
        try{
            instance = new StaticBlockSingleton();
           }catch(Exception e)
           {
                throw new RuntimeException("Exception occured in creating singleton instance");
           }
    }
    public static StaticBlockSingleton getInstance()
    {
        return instance;
    }
}

    和饥饿的实现方式同样,当类被classloader加载的时候就初始化了该对象。

【C】延迟建立

    顾名思义,就是须要的时候再去建立。

public class LazyInitializedSingleton 
{
    private static LazyInitializedSingleton instance;
    private LazyInitializedSingleton(){} 
    public static LazyInitializedSingleton getInstance()
    {
        if(instance == null)
        {
            instance = new LazyInitializedSingleton();
        }
        return instance;
    }
}

    上面这种实现方法,在单线程的环境下是工做良好的,可是,在多线程并发建立对象的时候会出现问题,简单的分析下缘由,考虑多线程并发安全的时候,首先要找到共享点,就是共享的对象,而后想象各类执行顺序,你会发现instance这个地方就是共享点,当多线程执行的时候,有可能会建立多个对象,因此该方法不能保证单例实现。

【D】线程安全的建立

    为了解决多线程并发建立对象的问题,咱们引入Synchronized关键字,这样的话,同一时刻,只有一个对象能访问getInstance方法。

public class ThreadSafeSingleton
{
    private static ThreadSafeSingleton instance;
    private ThreadSafeSingleton(){}
    public static synchronized ThreadSafeSingleton getInstance()
    {
        if(instance == null)
        {
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
}

    这种方式是能防止多并发引发的多对象建立问题,可是写代码嘛,总要追求点东西,你说别人装逼也好,卖弄也罢,总之在某一方面比你好。在多并发中,HashMap和ConcurrentHashMap,你是锁住整个HashMap仍是HashMap的一个segment这是有质的区别的,因此改进的代码,只朝着一个方向,那就是下降锁的力度,因而乎,下面的代码使用double check来下降了力度,提升了性能。

public static ThreadSafeSingleton getInstanceUsingDoubleLocking()
{
    if(instance == null)
    {
        synchronized (ThreadSafeSingleton.class) 
        {
            if(instance == null)
            {
                instance = new ThreadSafeSingleton();
            }
        }
    }
    return instance;
}

还有一种实现叫Bill Pugh,java的版本更新变化很快,包括新出的java9,可能名字不是,你们也知道,金融在将来几年是个很火的领域,因而乎java9中集成了货币的api支持,这是个人猜想,哈哈,包括先在的招财宝,铜板街,挖财,听说王福强大神,跳到挖财啦,固然我也是作金融行业的业务,有兴趣的能够留言,听说要招人。这些题外话,可是不无用处哈,java的内存模型会致使上面的几种方法,在不少不少线程出现的时候,会出现问题,因而乎,这我的,也就是Bill Pugh本身实现了一下这种方法:

【E】Bill Pugh实现

public class BillPughSingleton 
{
    private BillPughSingleton(){}
    private static class SingletonHelper
    {
        private static final BillPughSingleton INSTANCE = new
        BillPughSingleton(); 
    }
    public static BillPughSingleton getInstance()
    {
        return SingletonHelper.INSTANCE;
    }
}

看代码发现Bill Pugh是经过私有静态内部类来实现的,当单例对象被classloader加载的时候,SingletonHelper是不会被加载到内存的,除非有对象调用getInstance方法,这是最经常使用的建立单例对象的方法,由于不须要锁,已经在多个项目中使用了这种方法,简单有效。

【F】Enum实现

public enum EnumSingleton 
{
    INSTANCE;
    public static void doSomething()
    {
        //do something
    }
}

    世界上好人和坏人老是都存在的,单例对象也不例外,总有那么几种方法是搞破坏的,费尽千辛万苦,建立的单例,有可能被破坏的。

    【破坏者1】反射

    【破坏者2】序列化

有破坏办法,就又解决办法,破坏者1的解决办法是ENUM,破坏者2的解决办法是重写readResolve方法。这两部分,你说是和反射有关呢,仍是和序列化有关呢,序列化都够我写一篇东西的了。下次再说

                                        poke holes in the abstraction,and it starts leaking.

相关文章
相关标签/搜索