设计模式之 单例模式下

这一节我们来看看单例的其他创建方法。

上一节的懒汉式单例,用到了方法的同步,这在多线程并发场景下,无疑是性能非常差的,因为这相当于串行化了,所以我们可以做一些改进,来提升一下懒汉式单例的性能。

懒汉式单例–改进

package com.itzhimei.study.design.singleton;

/**
 * www.itzhimei.com
 * 懒汉式单例 改进
 */
public class Singleton3 {
    private static Singleton3 singleton = null;

    private Singleton3() {

    }

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

        return singleton;
    }

}
package com.itzhimei.study.design.singleton;

/**
 * www.itzhimei.com
 */
public class Client3 {

    public static void main(String[] args) {
        Singleton3 s1 = Singleton3.getInstance();
        Singleton3 s2 = Singleton3.getInstance();
        if(s1 == s2) {
            System.out.println("是一个对象");
        } else {
            System.out.println("不是一个对象");
        }
    }
}

输出:

是一个对象

改进的点就是将同步方法,修改为了同步代码块:

    if(singleton == null) {
        synchronized(Singleton3.class) {
            if(singleton == null) {
                singleton = new Singleton3();
            }
        }
    }

这样多线程访问,都不会阻塞在方法入口,而是都可以进入到方法,同时如果实例不为空,那么就直接返回实例了,并没有阻塞的发生,没有串行化;如果实例还没有被初始化,那么就会进入同步代码块,这是串行的,但是也只会在首次创建实例的时候发生同步,后续都不会产生同步,所以性能就提升了。

注意:有的人可能会说这种写法由问题,单例new出来还没来得及分配内存空间就被使用了,需要加volatile来修饰成员属性,其实,这是在低版本java中才会发生,新版本的已经修复这个问题了。(炫技的前提是要真的懂,要不就秀脱了,面试时就丢人了)。

静态内部类实现单例

package com.itzhimei.study.design.singleton;

/**
 * www.itzhimei.com
 * 静态内部类单例
 */
public class Singleton4InnerClass {
    private static Singleton4InnerClass singleton = null;

    private Singleton4InnerClass() {

    }

    private static class SInnerClass {
        private static final Singleton4InnerClass sic = new Singleton4InnerClass();
    }

    public static Singleton4InnerClass getInstance() {
        return SInnerClass.sic;
    }

}
package com.itzhimei.study.design.singleton;

/**
 * www.itzhimei.com
 */
public class Client4 {

    public static void main(String[] args) {
        Singleton4InnerClass s1 = Singleton4InnerClass.getInstance();
        Singleton4InnerClass s2 = Singleton4InnerClass.getInstance();
        if(s1 == s2) {
            System.out.println("是一个对象");
        } else {
            System.out.println("不是一个对象");
        }
    }
}

输出结果:

是一个对象

枚举类实现单例

其实枚举类天生就被Java设计成了单例,所以使用枚举类作为单例也是一种便捷的方法。

package com.itzhimei.study.design.singleton;

/**
 * www.itzhimei.com
 * 枚举单例
 */
public enum Singleton5Enum {
    instance
}

应用场景

单例一般多用来做资源加载,唯一id生成器。

在实际应用中,我们最熟悉的就是spring中,依赖注入,默认都是单例的。包括早期用到struct框架的controller也是单例的。