设计模式之单例模式

102 阅读1分钟

单例模式是指一个类只有一个实例,且该类能自行创建这个实例的一种模式。Spring中的Bean,数据库连接池,多线程连接池、Servlet都使用单例模式(Bean可以有多其他模式)。使用单例模式,有三个要点:私有化构造方法,定义静态实例、暴露实例方法。

单例模式主要有五种实现方法。

一、饿汉式

//饿汉式单例
public class Hungry {
    private Hungry() {
    }

    private static Hungry hungry = new Hungry();

    public static Hungry getInstance() {
        return hungry;
    }
}

这种方法在类加载时就初始化,造成内存的浪费。

二、懒汉式

/**
 * 懒汉式单例
 */
public class LazyMan {
    private LazyMan() {}

    private static LazyMan lazyMan;

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

这样写是线程不安全的,如果线程A执行到lazyMan==null时,CPU切换到了线程B,B执行了new LazyMan(),CPU又切换回A,此时,A和B都实例化对象了。可以在getInstance()方法上加锁解决,但是会造成比较大的性能消耗。

三、DCL双重锁定

只在实例第一次创建的时候才同步,提高了性能。

public class DCLSingle {
    private DCLSingle() {}

    private volatile static DCLSingle dclSingle;

    public static DCLSingle getInstance() {
        if (dclSingle == null) {
            synchronized (DCLSingle.class) {
                if (dclSingle == null) {
                    /**
                     * 不是原子操作,因此要加上volatile关键字,禁止指令重排
                     * 分配内存空间、执行构造方法,初始化对象、把这个对象指向空间
                     */
                    dclSingle = new DCLSingle();
                }
            }
        }
        return dclSingle;
    }
}

四、静态内部类

调用getInstance()时,InnerClass才会加载,才会创建实例,保证了延时加载。线程安全由JVM保证。

//静态内部类
public class Holder {
    private Holder() {}

    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }
}

五、枚举

//枚举
public enum  EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}