单例模式: 设计一个类,我们只能生成该类的一个实例

2023-06-20 11:13:06 浏览数 (2)


饿汉模式创建


饿汉模式是在类加载的时候就初始化实例

代码语言:javascript复制
package com.renzhikeji.demo.singleton;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public final class EagerInitializedSingleton {
    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

    private EagerInitializedSingleton() {
    }

    public static EagerInitializedSingleton getInstance() {
        return instance;
    }

    public void method() {
        System.out.println(this);
    }
}

实现机制:

1、用final修饰类,不让其被继承;

2、提前初始化的实例用private static final修饰;

3、构造函数私有,不被其他包的程序实例化;

4、提供静态方法获取实例;

5、实例初始化的线程安全,由类加载器内部保证;

缺点:

1、类被加载的时候就被初始化,即使实例没被程序使用;

2、反射可破坏;

3、实例初始化异常不可捕获;

静态初始化块创建


静态初始化和饿汉模式原理一样,不过我们可以处理创建异常的情况:

代码语言:javascript复制
package com.renzhikeji.demo.singleton;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public final class EagerInitializedSingleton {
    private static final EagerInitializedSingleton instance;

    static {
        try {
            instance = new EagerInitializedSingleton();
        } catch (Exception e) {
            throw new RuntimeException("Exception occured in creating singleton instance", e);
        }
    }

    private EagerInitializedSingleton() {
        System.out.println("init");
    }

    public static EagerInitializedSingleton getInstance() {
        return instance;
    }

    public void method() {
        System.out.println(this);
    }
}

缺点同饿汉模式,除了可以处理单例创建异常。

懒汉式创建-延迟初始化


当调用静态方法getInstance的时,创建实例:

代码语言:javascript复制
package com.renzhikeji.demo.singleton;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public final class Singleton {
    private staticSingleton instance;

    private Singleton() {
        System.out.println("init");
    }

    public static Singleton getInstance() {
        if (instance == null) {
            try {
                instance = new Singleton();
            } catch (Exception e) {
                throw new RuntimeException("Exception occured in creating singleton instance", e);
            }
        }
        return instance;
    }

    public void method() {
        System.out.println(this);
    }
}

缺点:

1、反射可破坏;

2、实例初始化非线程安全;

懒汉式创建-延迟初始化-线程安全版本


获取实例的静态方法用synchronized 修饰,使用内置监视器锁保证线程安全。

懒汉式创建-延迟初始化-线程安全版本-双重检查锁(Double Check Loc)


使用内置监视器锁保证线程安全,锁的粒度太大,每次调用获取实例方法都会使用锁,影响性能。为了避免每次调用获取实例方法都需要加锁,引入双重检查,检查实例是否初始化,没初始化,再用锁来安全的实例化:

代码语言:javascript复制
package com.renzhikeji.demo.singleton;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public final class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
        System.out.println("init");
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    try {
                        instance = new Singleton();
                    } catch (Exception e) {
                        throw new RuntimeException("Exception occured in creating singleton instance", e);
                    }
                }
            }
        }

        return instance;
    }

    public void method() {
        System.out.println(this);
    }
}

1、先检查单例是否已经初始化,没初始化的条件下,加锁;

2、加锁后还需要判断单例是否已经初始化,因为"先判断条件再执行动作"不在一个事务内,此时可能被其它线程已经初始化;

3、单例属性需要使用volatile来禁止重排序;

使用静态内部私有工具类初始化


代码语言:javascript复制
package com.renzhikeji.demo.singleton;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public final class Singleton {

    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHelper.instance;
    }

    private static class SingletonHelper {
        private static final Singleton instance = new Singleton();
    }

    public void method() {
        System.out.println(this);
    }
}

私有内部静态类持有外部单例的实例,getInstance方法直接返回此实例。当外部单例类Singleton被类加载的时,内部类SingletonHelper 是不会加载的,除非我们调用getInstance方法,触发内部类SingletonHelper的加载及单例的实例化(线程安全)。

枚举实现单例


以上实现单例的方式,都会遇到反射问题,导致单例失效。所以建议使用枚举实现:

代码语言:javascript复制
package com.renzhikeji.demo.singleton;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public enum EnumSingleton {
    instance;

    public void method() {
        System.out.println(this);
    }
}

枚举实现单例模式也有缺点:不太灵活,比如不允许延迟初始化。

单例模式与反序列化、反射


单例模式在反射、反序列化场景中,单例模式失效。

考点小结


1、单例模式的概念;

2、Java哪些关键字修饰可以避免类继承、实例化;

3、线程安全性;

4、单例与反序列化、反射;


0 人点赞