设计模式与应用:单例模式

本文介绍 常用的单例模式实现方式、使用场景、对各种实现优缺点探讨、笔者选用建议

简介

对象创建型模式

核心:一个类只生成唯一实例对象,构造方法私有化禁止外部创建对象。GoF对单例模式的定义是:保证一个类只有一个实例存在,同时提供能对该实例加以访问的全局访问方法(静态方法)
总结就是如下:
- 类中唯一实例引用
- 构造方法私有
- 一个静态方法访问该实例

为什么使用单例模式

  • 共享资源
  • 节省对象的创建时间等等

典型单例模式

  • 饿汉式
  • 懒汉式(饱汉)(多线程不能保证单例模式)
  • 双重检查
  • 静态内部类实现

多种实现方式

饿汉式

  • 优点:多线程中能够保证单例
  • 缺点:类一加载就实例化并且一直存在,浪费资源
  • 推荐:可以使用
package com.mym.designmodel.singleton;

/**
 * 饿汉单例模式
 */
public class ESingleton {
    public static ESingleton eSingleton = new ESingleton();

    private ESingleton(){}

    public static ESingleton getSingleton(){
        return eSingleton;
    }
}

饱汉式

  • 优点:能够懒加载
  • 缺点:多线程中不能保证单例
  • 推荐:不推荐使用
package com.mym.designmodel.singleton;

/**
 * 饱汉模式
 */
public class BSingleton {

    private static BSingleton bSingleton = null;

    private BSingleton(){}

    public static BSingleton getSingleton(){
        if(bSingleton == null){
            bSingleton  = new BSingleton();
        }
        return bSingleton;
    }

}

饱汉式线程安全

  • 优点:能够懒加载,保证了多线程中的单例
  • 缺点:阻塞模式,多线程执行效率较低
  • 推荐:不推荐使用
package com.mym.designmodel.singleton;

/**
 * 饱汉模式升级1
 */
public class BSingletonUp1 {

    private static BSingletonUp1 bSingletonUp1 = null;

    private BSingletonUp1(){}

    //阻塞模式
    public static synchronized  BSingletonUp1 getSingleton(){
        if(bSingletonUp1 == null){
            bSingletonUp1  = new BSingletonUp1();
        }
        return bSingletonUp1;
    }
}

双重检查模式单例实现

  • 优点:能够懒加载、保证多线程中的单例、微阻塞
  • 缺点:第一次访问仍然是阻塞的
  • 推荐:推荐使用,效率可以
package com.mym.designmodel.singleton;

/**
 * 双重检查模式
 */
public class DoubleCheckSingleton {

    private static DoubleCheckSingleton doubleCheckSingleton = null;

    private DoubleCheckSingleton(){}

    //双重检查 阻塞模式
    public static DoubleCheckSingleton getSingleton(){
        if(doubleCheckSingleton == null){
            synchronized (DoubleCheckSingleton.class){
                if(doubleCheckSingleton == null){
                    doubleCheckSingleton  = new DoubleCheckSingleton();
                }
            }
        }
        return doubleCheckSingleton;
    }
}

静态内部类实现单例模式

  • 优点:懒加载、多线程保证单例、非阻塞、效率高
  • 缺点:较其他实现方式多了一个类资源(静态内部类也是一个类),不过影响不大
  • 推荐:非常推荐,高效率
package com.mym.designmodel.singleton;

/**
 * 饱汉模式:静态内部类实现,非阻塞,推荐使用
 */
public class Singleton {

    private Singleton(){}

    private static class InnerClass{
        private final static Singleton instance = new Singleton();
    }

    public static Singleton getSingleton(){
        return InnerClass.instance;
    }

}

测试

本测试着力于是否保证单例。其他线程安全、执行效率,笔者就不贴代码了

package com.mym.designmodel.singleton;

/**
 * 测试
 */
public class MainClass {

    public static void main(String[] args) {
        ESingleton eSingleton1 = ESingleton.getSingleton();
        ESingleton eSingleton2 = ESingleton.getSingleton();
        System.out.println("【ESingleton】是否为同一实例:" + (eSingleton1 == eSingleton2));

        BSingleton bSingleton1 = BSingleton.getSingleton();
        BSingleton bSingleton2 = BSingleton.getSingleton();
        System.out.println("【BSingleton】是否为同一实例:" + (bSingleton1 == bSingleton2));

        BSingletonUp1 bSingletonUp1 = BSingletonUp1.getSingleton();
        BSingletonUp1 bSingletonUp2 = BSingletonUp1.getSingleton();
        System.out.println("【BSingletonUp1】是否为同一实例:" + (bSingletonUp1 == bSingletonUp2));

        DoubleCheckSingleton doubleCheckSingleton1 = DoubleCheckSingleton.getSingleton();
        DoubleCheckSingleton doubleCheckSingleton2 = DoubleCheckSingleton.getSingleton();
        System.out.println("【DoubleCheckSingleton】是否为同一实例:" + (doubleCheckSingleton1 == doubleCheckSingleton2));

        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();
        System.out.println("【Singleton】是否为同一实例:" + (singleton1 == singleton2));
    }
}

输出结果:

【ESingleton】是否为同一实例:true
【BSingleton】是否为同一实例:true
【BSingletonUp1】是否为同一实例:true
【DoubleCheckSingleton】是否为同一实例:true
【Singleton】是否为同一实例:true

单例模式的扩展

单例目的是节约资源。难点在于线程安全和效率。而单例不止是如上的实现,在java体系中,最为模范的单例模式就是枚举类,而最常用到的单例模式也还有八个基本数据类型对应的封装类:Integer、Long、Float…查看源码即可知道,他们内部都是单例且不可修改的:

Integer a = new Integer(2);
Integer b = new Integer(2);
System.out.println(a == b);

结果为false。

源码:Integer的内部

    /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

由封装类可知,实际上使用final也可以实现伪单例模式。

猜你喜欢

转载自blog.csdn.net/maoyuanming0806/article/details/80149861