java设计模式之--单例模式

单例模式是GoF设计模式其中的一种而且属于创造的设计模式目录。java

定义上,这彷佛是一种很是简单的设计模式,但从实现的角度来讲涉及到很是多的方面。
单例模式的实如今开发者中一直是个颇有争议的话题。数据库

这里咱们将学习单例模式原则,不一样的单例实现方式和最佳实践。设计模式

 

单例模式

单例模式限制类的实例和确保java类在java虚拟机中只有一个实例的存在。缓存

单例类必须提供一个全局的访问来获取类的实例。安全

单例模式用来日志,驱动对象,缓存和线程池。多线程

单例设计模式也用在其余设计模式,例如抽象工厂,建造者,原型,门面等设计模式。性能

单例模式还用在核心java中,例如java.lang.Runtime, java.awt.Desktop学习

 

java单例模式

为了实现Singleton模式,咱们有不一样的方法,但它们都有如下共同的概念。spa

  • 私有构造方法限制从其余类初始化类的实例。
  • 私有静态变量与该类的实例相同。
  • 公有静态方法返回类的实例,这是提供给外部访问的全局访问点来获取单例类的实例。在如下的章节,咱们将学习单例模式的不一样实现方法。

实现的类型线程

  • 饿汉模式
  • 静态初始化
  • 懒加载
  • 线程安全的单例
  • Bill Pugh单例实现(比尔·普格单例实现)
  • 枚举单例

 

饿汉模式

饿汉模式就是当类加载时就建立该类的实例,这是建立单例类最容易的方法可是有个一弊端是建立了该实例可是客户端程序可能不使用这个实例。

如下是静态初始化单例类的实现。饿汉式天生就是线程安全的。

public class EagerInitializedSingleton {

    private static EagerInitializedSingleton instance  = new EagerInitializedSingleton();

    private EagerInitializedSingleton(){

    }

    public static EagerInitializedSingleton getInstance(){
        return instance;
    }
}

 

在大多数的情景,单例类的建立是为了如文件系统,数据库链接等资源的管理。咱们应该避免过早的建立类的实例化,除非直到客户端调用getInstance()方法。
这种方法也没有提供异常处理的任何选项。

 

静态初始化块

静块初始化实现相似于饿汉模式初始化,但类的实例在静态代码块中建立并对异常进行处理。

public class StaticBlockSingleton {
    private static StaticBlockSingleton instance ;

    static {
        try{
            instance = new StaticBlockSingleton();
        }catch (Exception e){
            throw new RuntimeException("静态代码块中实例化失败");
        }

    }

    public static StaticBlockSingleton getInstance(){
        return instance;
    }

}

 

饿汉模式和静态初始化两种实现都是在实例在被使用以前就已经建立了,这不是最佳实践。
如下章节中,咱们将会学习如何建立支持懒加载的单例类。

 

懒加载

相比以前的两种方法,懒加载就是当须要的时候再来建立该类的实例,而不是一开始就把实例建立好了。

public class LazyInitializedSingleton {
    private static LazyInitializedSingleton instance = null;

    private LazyInitializedSingleton(){

    }

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

}

 

懒加载的实如今单线程环境中能够正常使用,可是在多线程环境中会引起一些问题。这将会破坏单例模式和线程会获取不一样的单例对象(不能保证线程安全) 。下文中,将会经过不一样方法来实现线程安全的单例类。

 

线程安全的单例

public class ThreadSafeSingleton {
    private static ThreadSafeSingleton instance = null;

    private ThreadSafeSingleton(){

    }

    //同步关键字synchronized
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return  instance;
    }

}

 

上述实现能正常运行,并提供了线程安全,但它下降了程序的性能,由于synchronized关键字修饰的是getInstance()整个方法。
但咱们须要它仅用于谁可能建立单独的实例的第一个线程数(阅读:Java同步)。为了不这种每一次的额外开销,使用双检查锁定原理。在这种方法中,synchronized块使用if条件里面加上一个额外的检查,以确保只建立一个单独的类的实例。

public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
    //synchronized同步块,控制更细的粒度
    if(instance == null){//此层的控制容许第一个线程进入访问,避免以上状况(同步方法)每次的等待开销。
        synchronized (ThreadSafeSingleton.class) {
            if(instance == null){//此处校验单例是否已经被建立,确实只有一个实例的存在。
                instance = new ThreadSafeSingleton();
            }
        }
    }
    return instance;
}

 

 

Bill Pugh单例

比尔普格想出了一个不一样的方法来建立一个使用静态内部辅助类的Singleton类。

public class BillPughSingleton {

    private static class SingleTonHelpClass{
        private static  final BillPughSingleton INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance(){
        return SingleTonHelpClass.INSTANCE;
    }

}

 

当单例类被加载,SingletonHelper内部类没有加载到内存中,只有当调用getInstance()方法时,该类被载入并建立Singleton类的实例。这种单例类由于它不要求使用同步的方法,容易理解和实现。

 

枚举单例

这种方法简单,便捷。

public enum  EnumSingleton {
    INSTANCE;

    public static void doSomething(){
        //do something
    }
}

转载请注明出处,原文连接https://zhuanlan.zhihu.com/p/20831029

相关文章
相关标签/搜索