单例设计模式简单介绍

12 阅读2分钟

饿汉式

//饿汉式:
public class SingleInstanceTest{
    //类加载的时候,提前创建好当前类对象
    private static final SingleInstanceTest INSTANCE = new Mgr01();
    
    //私有化无参构造器,使外界无法直接通过构造器创建该对象实例
    private SingleInstanceTest(){};
    
    //定义静态方法,获取初始化好的final实例对象,外界可在没有获取实例对象时,使用 类名.方法名的方式调用静态方法获取实例对象
    public static SingleInstanceTest getInstance(){
        return INSTANCE;
    }
    
    
    //测试获取到的 SingleInstanceTest 实例对象是否指向同一个地址
    public static void main(String[] args){
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
            System.out.println(SingleInstanceTest.getInstance().hashCode());
            }).start();
        }
        long end = System.currentTimeMillis();
        log.info("耗时:{}ms", end - start );
    }
}

懒汉式

//懒汉式:弊端,当多个线程同时访问时,会出现多个线程同时创建不同的实例对象,所以需要考虑并发时线程安全问题
public class SingleInstanceTest {
    //提前静态加载一个未初始化的空对象
    private static SingleInstanceTest INSTANCE ;

    //私有化构造方法
    private SingleInstanceTest() {}

    //输出当前实例对象的hashCode
    public void methodInstance() {
        System.out.println("方法内部实例化:" + this.hashCode());
    }
    
    /**
     * 测试方法一
     * 确保在并发情况下实现单例,方法整体加锁处理,缺点是:并发时候所有线程都需要等待锁的释放,耗时长
     * @return
     */
    public static synchronized SingleInstanceTest getInstance01() {
        if(ObjectUtil.isNull(INSTANCE)){
            INSTANCE = new SingleInstanceTest();
        }
        return INSTANCE;
    }

    /**
     * 测试方法二
     * 代码块加锁,只有部分并发线程数需要等待锁的释放,并发条件下相对于 测试方法一 缩短一定的耗时
     * @return
     */
    public static SingleInstanceTest getInstance02() {
        if(ObjectUtil.isNull(INSTANCE)){
            //代码块加锁,只有部分并发线程数需要等待锁的释放
            synchronized (SingleInstanceTest.class){
                if(ObjectUtil.isNull(INSTANCE)){
                    INSTANCE = new SingleInstanceTest();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 测试方法三
     * 静态内部类只有在第一次调用的时候才会被加载,且只加载一次
     * 静态内部类方式,不需要使用到锁的机制就能确保线程安全
     */
    private static class SingletonHolder{
        private static final SingleInstanceTest INSTANCE = new SingleInstanceTest();
    }

    public static SingleInstanceTest getInstanceByStaticInnerClass() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 测试结果数据 :方法二对代码块加锁 和 方法三静态内部内创建单例在并发下耗时相差不大;静态内部类相对显高级
     * @param args
     */
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
            //测试内部内的方式创建的单例是否是确保线程安全。
            System.out.println(SingleInstanceTest.getInstanceByStaticInnerClass());
            }).start();
        }
        long end = System.currentTimeMillis();
        log.info("耗时:{}ms", end - start );

    }
}

枚举类实现单例设计模式

//枚举类特殊性质:其内部定义的实例对象默认是静态且final修饰的,如下所示,就相当于饿汉式单例设计理念,
//枚举类简化了步骤,且枚举的特性可以防止反射穿透
public enum SingleInstanceTest2 {
//   public static final SingleInstanceTest2 INSTANCE = new SingleInstanceTest2();
    INSTANCE
    
    public void test() {
        System.out.println("testInstance:" + this.name() + ":" + this.hashCode());
    }

    public static void main(String[] args) {
        long l = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                SingleInstanceTest2.INSTANCE.hashCode();
            }).start();
        }
        System.out.println("time:" + (System.currentTimeMillis() - l) + "ms");
    }
}