java设计模式之单例模式

什么是单例模式

单例模式,顾名思义,就是某个类在整个系统运行过程中,只有一个实例。与之对应的是多例模式。
单例模式实现的关键点是构造方法私有,其他类不可以直接调用构造方法创建实例对象,这样需要 单例类自身创建实例 并且输出到整个系统中。保证只有一个实例的工作交由单例类来处理。

个人推荐下面第四种实现方式:使用类加载机制实现

模式实现

1. 懒汉模式(使用线程同步保证线程安全)

package designPattern.singleton;

public class Singleton {

    private static Singleton singleton = null;

    private Singleton(){}

    public synchronized static Singleton getInstance(){
        if(null == singleton){
            singleton = new Singleton();
        }
        return singleton;
    }
}

测试:

    public static void main(String[] args){
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();

        System.out.println(singleton == singleton1);
    }

输出结果为true。可见两个对象是同一个对象。

  • 优点
    实现简单;不会产生内存浪费;
  • 缺点
    并发量高的时候性能比较差;

2. 饿汉模式(创建一个类变量)

public class Singleton {

    private int a = 0;
    public void setA(int a) {        this.a = a;    }
    public int getA() {        return a;    }

    private static final Singleton singleton = new Singleton();

    private Singleton(){}

    public synchronized static final Singleton getInstance(){
        return singleton;
    }

    public static void main(String[] args){
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();

        System.out.println(singleton == singleton1);

        singleton.setA(10);
        System.out.println(singleton.getA());
        System.out.println(singleton1.getA());
    }
}

输出结果:

true
10
10
  • 优点:
    实现简单;不需要线程同步,效率高
  • 缺点:
    会产生内存浪费:在类初始化的时候就创建了对象保存在持久区中,即使没有任何引用。

3. 双重检查锁方式

public class Singleton {

    private static Singleton singleton = null;

    private Singleton(){}

    public static final Singleton getInstance(){
        if(null == singleton){
            synchronized (Singleton.class){
                if(null == singleton){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

双重检查锁方式是对上面懒汉模式的升级。

  • 优点
    理解简单;无内存浪费
  • 缺点
    JDK1.5以后才能保证真正的线程安全。不过现在没人再用1.5以前的老版本了,这个也可以不算缺点
    编写复杂

4. 使用类加载机制实现

public class Singleton {

    private Singleton(){}

    private static final class SingletonHolder{
        private static final Singleton SINGLETON = new Singleton();
    }

    public static final Singleton getInstance(){
        return SingletonHandler.SINGLETON;
    }
}

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance【不懂的同学请补习类加载机制】
想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。

  • 优点
    无锁,性能高;借助于类加载机制,代码优雅;可以实现懒加载,并且无内存浪费。
  • 缺点
    对类加载机制不了解可能驾驭不了这种写法。

JDK中的单例模式

单例模式是使用最为广泛的设计模式之一,JDK中也广泛使用,现在就HashSet为例简单说一下:

我们知道jdk中HashSet内部是使用HashMap实现的,那我们现在来看看HashSet里面的单例使用:

    private transient HashMap map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * Constructs a new, empty set; the backing HashMap instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

    //省略部分代码

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

new HashSet<>()的时候,其实是创建的一个HashMap对象,add方法就是往这个map中添加数据,我们往set中添加的数据,其实是作为key存储到了map中,map的value字段存储的就是一个虚拟值PRESENT,这里使用的就是上面说的恶汉模式。

多例模式

这里稍微说一下多例模式:在一些场景比如数据库连接池或者线程池等,一些类的创建销毁比较耗资源性能,需要缓存一部分实例在内存中,需要的时候就从池子里取一个实例来用,用完再归还到池子中。

通常是使用list等数据结构将创建的对象缓冲到JVM内存中。

你可能感兴趣的:(设计模式)