zoukankan      html  css  js  c++  java
  • 设计模式--单例模式

    单例模式

    单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。

    应用场景

    Spring框架应用中的ApplicationContext、数据库连接池、JDK中Runtime类等。

    public class Runtime {
        private static Runtime currentRuntime = new Runtime();
    
        public static Runtime getRuntime() {
            return currentRuntime;
        }
    
        private Runtime() {
        }
    }
    

    饿汉式

    /**
     * 优点:线程绝对安全。执行效率比较高
     * 缺点:所有对象加载的时候就要实例化,如果有大批量单例对象创建,占用大量内存,浪费内存资源
     */
    public class HungrySingleton {
    
        private static final HungrySingleton singleton = new HungrySingleton();
    
        private HungrySingleton() {
        }
    
        public static HungrySingleton getInstance() {
            return singleton;
        }
    }
    

    懒汉式

    /**
     * 优点:提高内存使用
     * 缺点:使用锁,降低执行速度
     */
    public class LazySingleton {
    
        private LazySingleton() {
        }
    
        private static volatile LazySingleton singleton;
    
        public static LazySingleton getInstance() {
            //检查是否要阻塞
            if (singleton == null) {
                synchronized (LazySingleton.class) {
                    //判断是创建新的对象
                    if (singleton == null) {
                        singleton = new LazySingleton();
                        //设置lazy指向刚分配的内存地址
                        //指令重排序的问题
                    }
                }
            }
            return singleton;
        }
    }
    

    静态内部类单例

    package com.example.springtools.util.singleton;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    
    /**
     * 1.使用静态内部类创建 兼顾饿汉式单例模式的内存浪费问题和synchronized的性能问题
     * 内部类在方法调用之前初始化,避免产生线程安全问题
     * 2.构造方法加入对象判断条件 防止使用反射调用构造方法创建多个对象,破坏单例
     * 3.使用readResolve方法,防止反序列化的问题
     * 通过查看源码得知当对象调用readObject读取对象产生对象时,如果发现对象内部有readResolve,则返回readResolve方法的返回值,
     * 如果不存在,则创建新的对象
     */
    public class LazyInnerClassSingleton {
    
        private LazyInnerClassSingleton() {
            if (LazyHolder.LAZY != null) {
                throw new RuntimeException("不允许创建多个实例");
            }
        }
    
        public static LazyInnerClassSingleton getInstance() {
            return LazyHolder.LAZY;
        }
    
        private static class LazyHolder {
            private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
        }
    
    //    序列化
    //    FileInputStream fis = new FileInputStream("xx");
    //    ObjectInputStream ois = new ObjectInputStream(fis);
    //    LazyInnerClassSingleton o = (LazyInnerClassSingleton)ois.readObject();
    //    ois.close();
    
        public LazyInnerClassSingleton readResolve() {
            return LazyHolder.LAZY;
        }
    }
    

    注册式单例模式(登记式单例模式)

    枚举式单例模式

    /**
     * 最优雅的方式
     * 通过查看源码发现枚举采用的是饿汉式单例模式的创建方法,当然也存在内存资源消耗的问题,不适合大量单例对象的创建
     * 并发安全、不会被反射和反序列化破坏
     */
    public enum EnumSingleton {
    
        INSTANCE;
    
        private Object data;
    
        private Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public static EnumSingleton getInstance() {
            return INSTANCE;
        }
    }
    

    容器式单例模式

    /**
     * 类似懒汉式,线程不安全
     */
    public class ContainerSingleton {
    
        private ContainerSingleton() {
        }
    
        private static Map<String, Object> ioc = new ConcurrentHashMap<>();
    
        public static Object getBean(String className) {
            synchronized (ioc) {
                if (ioc.containsKey(className)) {
                    Object obj = null;
                    try {
                        obj = Class.forName(className).newInstance();
                        ioc.put(className, obj);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return obj;
                } else {
                    return ioc.get(className);
                }
            }
        }
    }
    

    线程单例实现ThreadLocal

    /**
     * ThreadLocal不能保证全局唯一,但是可以保证单个线程唯一,天生线程安全
     * 每个对象都会放到ThreadLocalMap中,为每个线程提供一个对象,空间换实现实现线程隔离。
     */
    public class ThreadLocalSingleton {
    
        private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
                new ThreadLocal<ThreadLocalSingleton>() {
                    @Override
                    protected ThreadLocalSingleton initialValue() {
                        return new ThreadLocalSingleton();
                    }
                };
    
        private ThreadLocalSingleton() {
        }
    
        public static ThreadLocalSingleton getInstance() {
            return threadLocalInstance.get();
        }
    
    }
    
    

    总结:

    ​ 单例模式可以保证内存里只有一个实例,减少内存的开销,还可以避免对资源的多重占用。

  • 相关阅读:
    贝叶斯公式推导
    三种常量池
    SpringCloud使用Feign实现服务间通信
    springCloud配置本地配中心SpringCloudConfig
    SpringApplication执行流程
    调用shutdown.sh后出现could not contact localhost8005 tomcat may not be running报错问题
    TCP协议详解
    web.xml配置说明
    第一份offer
    博客CSS
  • 原文地址:https://www.cnblogs.com/snail-gao/p/14683480.html
Copyright © 2011-2022 走看看