架构学习之路(一)源码分析 3 单例模式

11 篇文章 0 订阅

架构学习之路(一)源码分析 2 简单工厂模式与工厂方法模式

  1. 单例模式(Singleton)

    1. 创建型

    2. 保证从系统启动到系统终止,全过程只会产生一个实例,并提供一个访问它的全局访问点

    3. 初衷是使资源共享,只需要初始化一次,所有人都可以重复使用

    4. 当我们在应用中遇到功能性冲突的时候,需要使用单例模式

    5. Spring 没有从构造器级别去控制单例,因为 Spring 管理的是任意的 Java 对象

    6. Spring 下默认的 Bean 均为单例

    7. 解决并发访问时候的线程安全问题,序列化与反序列化的时候出现多例

    8. 构造方法私有化

    9. 生活场景

      1. 配置信息Config
      2. 日历Calender
      3. IOC 容器
      4. Listener
    10. 模式

      1. 饿汉式

        1. 在类加载的时候就立即初始化,并且创建单例对象。
        2. 优点
          1. 没有加锁,执行效率高。
          2. 用户体验好。
          3. 线程安全,线程还没创建之前,就已经创建好实例。
        3. 缺点:占用资源
        public class Hungry {
        	private Hungry(){ }
        	private static final Hungry hungry = new Hungry();
        	
        	public static Hungry getInstance(){
        		return hungry;
        	}
        }
        
      2. 懒汉式

        1. 默认在加载的时候不实例化,在第一次使用的时候才实例化。
        2. 线程不安全。
        3. Spring 延时加载。
        /**
         * 线程不安全
         **/
        public class Lazy {
        	private Lazy(){}
        	private static Lazy lazy;
        	public static Lazy getInstance(){
        		if(lazy == null) lazy = new Lazy();
        		return lazy;
        	}
        }
        
        /**
         * 线程安全,效率低
         **/
        public class Lazy2 {
        	private Lazy2(){}
        	private static Lazy2 lazy;
        	public static synchronized Lazy2 getInstance(){
        		if(lazy == null) lazy = new Lazy2();
        		return lazy;
        	}
        }
        
        /**
         * 在外部类被调用时内部类才加载,避免了线程安全问题
         * 这种形式兼顾饿汉式的内存浪费,也兼顾 synchronized 性能问题,完美的屏蔽了这两个缺点
         * 比较完美的单例模式实现方式
         **/
        public class Lazy3 {
        	private static boolean initialized = false;
        	private Lazy3(){
                /**
                 * 避免反射入侵
                 */
        		if (initialized) throw new RuntimeException("请使用 Lazy3.getInstance() 获取实例对象。");
        		initialized = true;
        	}
            /**
             * static:空间共享
             * final:保证方法不会被重写,重载
             * 在返回结果以前一定会先加载内部类
             */
        	public static final Lazy3 getInstance(){
        		return LazyHolder.LAZY;
        	}
        	private static class LazyHolder{
        		private static final Lazy3 LAZY = new Lazy3();
        	}
        }
        
      3. 注册式

        1. 首次使用在固定容器中注册,并且将使用过的对象进行缓存,下次取对象直接从缓存中取值,以保证每次获取的都是同一个对象。
        2. IOC 中的单例模式
        public class BeanFactory {
        	private BeanFactory(){}
        	// 线程安全
        	private static final Map<String,Object> ioc = new ConcurrentHashMap<>();
        	public static Object getInstance(String className){
        		if(ioc.containsKey(className)) return ioc.get(className);
        		// Class.forName(className).newInstance()在 jdk1.9 中弃用
        		try {
        			Constructor<?> clazz = Class.forName(className).getDeclaredConstructor();
        			clazz.setAccessible(true);
        			ioc.put(className,clazz.newInstance());
        			return ioc.get(className);
        		} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
        			e.printStackTrace();
        			throw new RuntimeException("Bean 不存在于 IOC 中");
        		}
        	}
        }
        
        /**
         * 注册枚举,常量中去使用
         **/
        public enum ColorEnum {
        	WHITE(){
        		private int r = 255;
        		private int g = 255;
        		private int b = 255;
        	},BLACK(){
        		private int r = 0;
        		private int g = 0;
        		private int b = 0;
        	};
        }
        
      4. 序列化:重写 readResolve() 保证单例

        /**
         * 防止反序列化时导致单例破坏
         * 序列化:把内存中的状态通过转换成字节码的形式(IO 流),写入到其他地方,内存中状态被永久保存
         * 反序列化:将已经持久化的字节码内容转换为 IO 流,通过 IO 流将读取内容转换为 Java 对象
         * 在转换过程中会重新创建对象
         */
        public class Serial implements Serializable {
        	public final static Serial INSTANCE = new Serial();
        	private Serial(){}
        	public static Serial getInstance(){
        		return INSTANCE;
        	}
        	private Object readResolve(){
        		return INSTANCE;
        	}
        }
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值