0
点赞
收藏
分享

微信扫一扫

单例模式---饿汉式、懒汉式、枚举方式

Just_Esme 2022-03-31 阅读 144
java

一、懒汉式

懒汉式是对象用不用都已经创建了,这样比较浪费内存。

public class Hungry {
    
    //重要:构造器私有化
    private Hungry(){

    }
    
    //创建静态对象,静态总在内存中加载一份
    public static final Hungry hungry =new Hungry();
    //获取对象方法
    public static Hungry getInstance(){
        return hungry;
    }
}

二、懒汉式DCL

可以理解为我什么时间用什么时间创建对象,节省内存。

/**
 * 懒汉式--单例模式
 */
public class Lazy {

    private Boolean flag=false;

    private Lazy(){
        synchronized (Lazy.class){
            if (flag==false){
                flag=true;
            }else {
                throw  new RuntimeException("反射破解异常报错");
            }
        }

    }

    public volatile static  Lazy lazy;//用volatile修饰,禁止指令重排

    /**
     * 双重检测模式 DCL懒汉式
     * @return
     */
    public static Lazy getInstance(){

        if (lazy==null){
            //针对多线程加锁,总是一个对象
            synchronized (Lazy.class){

                //第一个线程判空成功之后,使用synchronized关键字对赋值操作进行加锁,第二次判空之后开始了创建对象并赋值的操作
                if (lazy == null) { //对于单线程是安全的,一个对象
                    //原子性是指在一个操作中,cpu不可以在中途暂停然后再调度,要么一次执行完成,要么就不执行。
                    lazy = new Lazy(); //不是原子性操作
                    /**
                     * 注意,这里的操作并不是原子性的,简单来说,这里的操作可以分成三步:
                     * 1.创建内存空间
                     * 2.在内存空间内创建对象
                     * 3.将内存空间赋值给变量lazy
                     */

                    /**
                     * 上面的操作由于不是原子性,所以三步之间是可以被中断的,再加上不是原子操作,
                     * 所以可能会进行重排序,所以又产生了有序性问题
                     * 如果将步骤2和步骤3进行了重排序,创建完内存之后立刻赋值,赋值之后再进行对象的创建,
                     * 而另外一个线程在赋值和对象的创建之间对变量singleton 进行了访问,那么他就会拿到一个半成品的对象。
                     * 这时就会出现空指针异常。这个问题是由顺序性和原子性两个原因共同导致的。
                     *
                     * 所以synchronized关键字对原子性的保证是从结果上保证的,因为对于整个赋值操作,无论是否重排序,
                     * 确实没有影响结果,但是对于另外一个线程来讲就不尽相同了。
                     */
                }
            }
        }
        return lazy;
    }
}

三、反射破坏单例模式(暴力反射)

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Lazy lazy1 = Lazy.getInstance();
        System.out.println(lazy1);
        // 采用反射,可以破坏懒汉式单例
        Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor(null);
        // 设置构造器可以外部访问
        constructor.setAccessible(true);
        Lazy lazy = constructor.newInstance();
        System.out.println(lazy);
    }

输出结果是两个不同的实例对象。

四、枚举实现单例模式

源码如下:

    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)

            //不能通过反射创建枚举对象

            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

利用枚举实现单例代码:

public enum SinglePattern {
    INSTANCE;

    private SinglePattern() {
    }
    public static SinglePattern GetInstance() {
        return INSTANCE;
    }
}
举报

相关推荐

0 条评论