23种设计模式之单例设计模式

基于渡一教育拓哥单例设计模式课程讲解,及大佬文章、个人理解。

应用背景:解决对象创建的问题,让当前类只能创建一个对象

在当前类的内部创建一个对象,构造方法放置的位置分析

  • 属性 :可取

  • 方法:多次调用会创建多个对象,无意义

  • 构造方法:构造方法内嵌套构造方法,无意义

  • 代码块:没有返回值,不可取

1. 构造方法私有
2. 创建一个私有的静态的当前类对象作为属性,用来存储唯一的一个对象
3.公有的静态方法,用来将唯一的这个对象返回

注意:如果属性不添加static关键字,开辟对象空间时会调用构造方法,同时将该类对象作为属性,造成递归调用构造方法,由于对象的方法调用是在栈内存中,会产生StackOverFlowError,栈内存溢出问题。

1.饿汉式(立即加载的形式)

为避免通过反射改变该属性的值,添加final关键字修饰。

public class SingleTon {
    private static final SingleTon singleTon=new SingleTon();
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        return singleTon;
    }
}

分析:对象提前加载,可能存在内存消耗。

2.懒汉式(延迟加载的形式)

public class SingleTon {
    private static SingleTon singleTon=null;
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        if (singleTon==null){               //代码1
            singleTon=new SingleTon();      //代码2
        }
        return singleTon;
    }   
}

分析:如果两个线程A,B同时访问,线程A执行代码1的同时,B线程执行代码2。A线程会看到singleTon的引用对象还未完成初始化(线程并发的可见性)。

public class SingleTon {
    private static SingleTon singleTon=null;
    private SingleTon(){}
    public static synchronized SingleTon getSingleTon(){
        if (singleTon==null){
            singleTon=new SingleTon();
        }
        return singleTon;
    }   
}

分析:采用线程锁,避免并发访问,虽然保证安全,但多次调用,性能低。

3.基于类初始化(延迟加载的形式)

public class InstanceFactory{
    /**
    *类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
    *没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载
    */
        private static class InstanceHolder{
            public static Instance instance = new Instance();
        }
        public static Instance getInstance(){
            return InstanceHolder.instance;//这里将导致InstanceHolder类被初始化
        }
}

4.双重检测模式

public class SingleTon {
    private static SingleTon singleTon=null;
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        if (singleTon==null){  
            synchronized (SingleTon.class){
                if (singleTon==null){
                    singleTon=new SingleTon();   //代码1
                }
            }
        }
        return singleTon;    //代码2
    }
}

分析:代码1可以分解为如下的3行伪代码。

memory=allocate();        //1:分配对象的内存空间
ctorInstance(memory);     //2:初始化对象
instance = memory;        //3:设置instance指向刚分配的内存地址

上面3行伪代码中的2和3,可能会重排序。2和3重排序的执行时序如下。

memory=allocate();        //1:分配对象的内存空间
instance = memory;        //3:设置instance指向刚分配的内存地址
                          //注意,此时对象还没有被初始化!
ctorInstance(memory);     //2:初始化对象

如果两个线程A,B同时访问,由于双重检测不会产生冲突,但可能线程A执行代码1的同时,线程执行代码2,但此时singleTon引用的对象可能还未完成初始化。

引入voatile修饰符,修饰属性,避免jvm指令重排序,规定执行顺序,同时保证线程的可见性。

public class SingleTon {
    private volatile static  SingleTon singleTon=null;
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        if (singleTon==null){
            synchronized (SingleTon.class){
                if (singleTon==null){
                    singleTon=new SingleTon();
                }
            }
        }
        return singleTon;
    }
}

5.生命周期托管方式

利用spring框架的IOC控制权翻转,以及DI依赖注入的思想,使用反射技术,以及hashmap作为缓存存储实现。

MyManager.java

import java.util.HashMap;
 
/**
 * 为了管理对象的产生
 * 对象的控制权给当前类负责
 * 生命周期托管实现对象的单例
 * IOC控制反转
 */
public class MyManager {
    // 存储所有被管理的对象
    private static HashMap<String, Object> beanMap = new HashMap<>();
 
    // 获取任何类的一个对象
    // className : 类名
    // return: (泛型T)
    public static <T> T getBean(String className) {
        T obj = null;
        try {
            // 去beanBox看有没有存在了
            obj = (T) beanMap.get(className);
            if (obj == null) {
                // 1. 通过类名获取类
                Class clazz = Class.forName(className);
                // 2. 通过反射产生一个对象
                obj = (T) clazz.newInstance();
                // 3. 加入集合
                beanMap.put(className, obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}

Sign.java

// 单例模式 IOC
// 生命周期给别人托管
public class Sign {
    public Sign() {
        System.out.println("创建了一个实例");
    }
}

6.枚举类型实现

枚举类型实现极其简单,只需编写一个包含单个元素的枚举类型即可,不过多赘述。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值