单例如何创建,你知道几种形式?

“单例如何创建,你知道几种形式?”面试官鬼魅一笑……
还好我想起来了看过的那本“宝典”!


关于代码中单例的创建,子涵先生总结了6种常见的方式。
在这里插入图片描述

饿汉式

直接创建对象,不存在线程安全问题。
关键要素:

  • 一个类只能有一个实例——构造器私有化;
  • 必须自行创建这个实例——该类的静态变量;
  • 必须自行向整个系统提供这个实例——(1)直接暴露;(2)静态变量的get方法;

直接实例化饿汉式(简洁直观)

public class Singleton1 {
	// 使用public直接暴露
    public static final Singleton1 INSTANCE = new Singleton1();
    private Singleton1() {
    }
    // 使用public方法直接暴露
    public static Singleton1 getInstance(){
        return INSTANCE;
    }
}

枚举式(最简洁)

/**
 * 推荐
 */
public enum SingletonEnum {
    INSTANCE
}

静态代码块饿汉式(适合复杂实例化)

/**
 * 基于外部配置的单例
 */
public class SingletonStaticBlock {
    public static final SingletonStaticBlock INSTANCE;
    private String info;
    static {
        Properties por = new Properties();
        try {
            por.load(SingletonStaticBlock.class.getClassLoader().getResourceAsStream("single.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        INSTANCE = new SingletonStaticBlock(por.getProperty("hello"));
    }

    private SingletonStaticBlock(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

resource下增加文件single.properties

hello = Mr.zihan

接下来我们测试一下~

    @Test
    public void testSingleton(){
//        Singleton singleton = new Singleton();//无法new
//        Singleton1 singleton1 = new Singleton1();//无法new
        System.out.println(SingletonEnum.INSTANCE);
        System.out.println(Singleton1.INSTANCE);
        System.out.println(SingletonStaticBlock.INSTANCE.getInfo());
    }

测试结果:

INSTANCE
com.dwlijx.pattern.creation.single.Singleton1@eec5a4a
Mr.zihan

懒汉式

延迟创建对象。
要点:

  • (1)构造器私有化
  • (2)静态变量保存唯一实例;
  • (3)提供一个静态方法,获取这个实例的对象

线程不安全(适用于单线程)

public class SingletonLazy {
	// 需要时再实例化
    private static SingletonLazy INSTANCE;
    private SingletonLazy() {
    }

    public static SingletonLazy getINSTANCE(){
        if (null == INSTANCE){
            INSTANCE = new SingletonLazy();
        }
        return INSTANCE;
    }
}

缺点:线程不安全,适用于单线程场景。
测试一下:
可能需要多试几次。

/**
     * 懒汉模式
     * 用两个线程getInstance,比对一下实例的hashCode
     */
    @Test
    public void securityTest() throws ExecutionException, InterruptedException {
        Callable<SingletonLazy> callableTask = new Callable<SingletonLazy>() {
            @Override
            public SingletonLazy call() throws Exception {
                return SingletonLazy.getINSTANCE();
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<SingletonLazy> f1 = es.submit(callableTask);
        Future<SingletonLazy> f2 = es.submit(callableTask);

        System.out.println(f1.get().hashCode());
        System.out.println(f2.get().hashCode());
        es.shutdown();
    }
1316864772
1685232414

class加锁保证线程安全(适用于多线程)

在上一个例子基础上增加了控制多线程安全的手段。

public class SingletonLazySafe {
    private static SingletonLazySafe instance;
	//构造方法私有
    private SingletonLazySafe() {
    }

    public static SingletonLazySafe getInstance(){
        // 双重校验锁提升效率
        if (null == instance){
            synchronized (SingletonLazySafe.class){
                if (null == instance){
                    instance = new SingletonLazySafe();
                }
            }
        }
        return instance;
    }
}

测试一下

1316864772
1316864772

那么问题来了,你知道为啥每次初始化,hashCode都是相同的值吗?比如1316864772

静态内部类形式(适用于多线程)

(1)构造器私有化
(2)内部类的静态变量保存唯一实例;
利用了内部类的加载特性:内部类不会自动随着外部类的加载和初始化而初始化,而是单独去加载和初始化的。

public class InnerClassLazySingleton {

    private InnerClassLazySingleton() {
    }

    public static class Inner{
        private static InnerClassLazySingleton INSTANCE = new InnerClassLazySingleton();
    }

    public static InnerClassLazySingleton getInstance(){
        return Inner.INSTANCE;
    }
}

测试一下

/**
     * 懒汉模式-解决线程安全问题
     * 用两个线程getInstance,比对一下实例的hashCode
     */
    @Test
    public void innerClassSingletonSafeTest() throws ExecutionException, InterruptedException {
        Callable<InnerClassLazySingleton> callableTask = new Callable<InnerClassLazySingleton>() {
            @Override
            public InnerClassLazySingleton call() throws Exception {
                return InnerClassLazySingleton.getInstance();
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<InnerClassLazySingleton> f1 = es.submit(callableTask);
        Future<InnerClassLazySingleton> f2 = es.submit(callableTask);

        System.out.println(f1.get().hashCode());
        System.out.println(f2.get().hashCode());
        es.shutdown();
    }

测试结果:

1285044316
1285044316

在这里插入图片描述

感谢您的赏读。客官,点赞、留言再走呗~或者留下您的问题我们一起探讨~

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子涵先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值