java设计模式之单例模式

前几天看到一个问题,问单例模式有几种实现方式,由此今天来说一下这个问题。

首先说说什么是单例模式,它是指JAVA类有且只有一个全局访问点,保证该类只创建一个实例。单例模式有三个基本的要点:

1、该类只有一个实例。

2、这个实例必须是自行创建。

3、必须自行为整个系统提供该实例的访问。

一般情况下,单例模式分区饿汉模式懒汉模式;饿汉模式会在类初始化的时候就开辟一块内存空间,它保证了多线程下实例的唯一性,并且效率非常高;但是缺点在于当类的成员比较多或者变量比较大的时候就开辟了空间对资源的消耗就比较大。为了避免饿汉模式的这个问题,懒汉模式会在系统使用到这个类是才去创建这个对象。

常规实现

饿汉模式:
public final class Singleton {
    /**
     * 自行创建私有实例
     */
    private static Singleton instance = new Singleton();
    /**
     * 私有构造
     */
    private Singleton() {
    }
    /**
     * 提供公有的全局访问点
     * @return
     */
    public static Singleton getInstance() {
        return instance;
    }
}
懒汉模式:
public final class Singleton {
    /**
     * 自行创建私有实例
     */
    private static Singleton instance = null;
    /**
     * 私有构造
     */
    private Singleton() {
    }
    /**
     * 提供公有的全局访问点
     * @return
     */
    public static Singleton getInstance() {
        if(instance==null){
            instance = new Singleton();
        }
        return instance;
    }
}

以上代码是饿汉模式和懒汉模式的常用实现代码;但是以上懒汉模式是存在问题的,试想,在多线程的场景下运行,同时执行到这段代码时,可能会实例化多个实例。
这时候,我们可以添加synchronized来同步锁修饰getInstance方法,保证其只被实例化一次

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

但是,这样做了之后还是有问题的,使用同步锁会带来锁竞争,增加系统的性能开销,这时候可以考虑将synchronized添加在if条件中:

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

这样,感觉是完美了;但如果两个线程同时进入了if条件里面,虽然有同步锁,但是进入到条件里面的线程依然会依次获取锁创建对象,然后再释放同步锁,所以在if条件里面还需要添加if条件的判断

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

这样,通过synchronized+两次if就实现了一个稳定的单例模式结构,但是此种结构比较复杂,有没有简化一点的呢?

内部类实现

饿汉模式中,使用static修饰的instance,所以类在初始化完成就已经被收集到类构造器中了。在多线程情况下,JVM会保证只有一个线程能执行这个方法,其他线程都会被阻塞等待。这种方式保证了内存的可见性、顺序性、原子性。
如果通过一个内部类来完成成员的实例化,则也可以避免多线程重复创建实例的情况:

public final class Singleton {
    /**
     * 私有构造
     */
    private Singleton() {
    }
    /**
     * 提供公有的全局访问点
     * @return
     */
    public static  Singleton getInstance() {
        return InsideSingleton.instance;
    }
    /**
     * 实例
     */
    public static class InsideSingleton {
        private static Singleton instance = new Singleton();
    } 
}

枚举实现

相较于饿汉模式懒汉模式,枚举是实现方式就会更加简洁

public final class Singleton {
    /**
     * 私有构造
     */
    private Singleton() {
    }
    /**
     * 提供公有的全局访问点
     * @return
     */
    public static Singleton getInstance() {
        return SinletonEnum.SINLETON.getInstance();
    }
    /**
     * 定义单例枚举
     */
    private enum SinletonEnum {
        SINLETON;
        private Singleton singleton;

        SinletonEnum() {
            singleton = new Singleton();
        }
        /**
         * 构建实例
         */
        public Singleton getInstance() {
            return singleton;
        }
    }
}

你可能感兴趣的:(java设计模式之单例模式)