赞
踩
目录
(一)枚举式单例模式代码及分析:(Effective Java推荐单例模式)
(二)容器式单例模式代码及分析:(适用于实例非常多的情况,便于管理,但是是非线程安全的)
干货分享,感谢您的阅读!
单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
该模式有三个基本要点:
应用场景:J2EE中的ServlertContext、SerletContextConfig等、Spring框架应用中的ApplicationContext、数据库连接池等。
- /**
- * 描述:饿汉式单例模式
- * 优点:没有任何锁,执行效率高,用户体验比懒汉式单例模式更好
- * 缺点:类加载的时候就初始化,不管用不用都占内存空间
- * 建议:适用于单例模式较少的场景
- * 如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
- * 如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。
- *
- * @author yanfengzhang
- * @date 2020-01-02 20:39
- */
- public class HungrySingleton {
- private final static HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
-
- private HungrySingleton() {
- }
-
- public static HungrySingleton getInstance() {
- return HUNGRY_SINGLETON;
- }
- }
优点:没有任何锁,执行效率高,用户体验比懒汉式单例模式更好
缺点:类加载的时候就初始化,不管用不用都占内存空间
建议:
- /**
- * 描述:懒汉式单例模式---双重检查锁
- * 相比单锁而言,双重检查锁性能上虽然有提升,但是依旧用到了synchronized关键字总归要上锁,对程序性能还是存在一定的性能影响
- * 不算最优--存在优化空间
- *
- * 建议:如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
- * 如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。
- *
- * @author yanfengzhang
- * @date 2020-01-02 20:53
- */
- public class LazyDoubleCheckSingleton {
- /**
- * volatile 关键字可以保证线程间变量的可见性,还有一个作用就是阻止局部重排序的发生
- */
- private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
-
- private LazyDoubleCheckSingleton() {
- }
-
- public static LazyDoubleCheckSingleton getInstance() {
- if (null == lazyDoubleCheckSingleton) {
- synchronized (LazyDoubleCheckSingleton.class) {
- if (null == lazyDoubleCheckSingleton) {
- lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
- }
- }
- }
- return lazyDoubleCheckSingleton;
- }
- }
相比单锁而言,双重检查锁性能上虽然有提升,但是依旧用到了synchronized关键字总归要上锁,对程序性能还是存在一定的性能影响。注意里面volatile的使用!!!
建议:
- /**
- * 描述:屏蔽饿汉式单例模式的内存浪费问题和双重检查锁中synchronized的性能问题
- * 避免因为反射破坏单例
- *
- * @author yanfengzhang
- * @date 2020-01-02 21:08
- */
- public class LazyInnerClassSingleton {
- /**
- * 使用LazyInnerClassSingleton的时候会先默认初始化换内部类
- * 如果没有使用,则内部类是不加载的
- */
- private LazyInnerClassSingleton() {
- /*为了避免反射破坏单例,需要在构造方法中增加限制,一旦出现多次重复创建,直接抛出异常*/
- if (null != Lazyholder.LAZY_INNER_CLASS_SINGLETON) {
- throw new RuntimeException("创建LazyInnerClassSingleton异常,不允许创建多个实例!");
- }
- }
-
- /**
- * 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个方法不会被重写、重载
- */
- public static final LazyInnerClassSingleton getInstance() {
- /*在返回结果前,一定会先加载内部类*/
- return Lazyholder.LAZY_INNER_CLASS_SINGLETON;
- }
-
- /**
- * 默认不加载
- */
- private static class Lazyholder {
- private static final LazyInnerClassSingleton LAZY_INNER_CLASS_SINGLETON = new LazyInnerClassSingleton();
- }
- }
屏蔽饿汉式单例模式的内存浪费问题和双重检查锁中synchronized的性能问题,同时考虑避免因为反射破坏单例问题。
相对而言性能最好!
注册式单例模式/登记式单例模式,将每个实例都登记到一个地方,使用唯一的标识获取单例。
注册单例模式有两种:枚举式单例模式+容器式单例模式
- /**
- * 描述:注册式单例模式/登记式单例模式,将每个实例都登记到一个地方,使用唯一的标识获取单例。
- * 注册单例模式有两种:枚举式单例模式+容器式单例模式
- * 此为枚举式单例模式---Effective Java推荐单例模式
- *
- * @author yanfengzhang
- * @date 2020-01-03 09:59
- */
- public enum EnumSingleton {
- /*枚举式单例模式*/
- INSTANCE;
-
- private Object data;
-
- public Object getData() {
- return data;
- }
-
- public void setData(Object data) {
- this.data = data;
- }
-
- public static EnumSingleton getInstance() {
- return INSTANCE;
- }
- }
- /**
- * 描述:注册式单例模式/登记式单例模式,将每个实例都登记到一个地方,使用唯一的标识获取单例。
- * 注册单例模式有两种:枚举式单例模式+容器式单例模式
- * 建议:容器式单例模式适用于实例非常多的情况,便于管理,但是是非线程安全的。
- *
- * @author yanfengzhang
- * @date 2020-01-03 10:51
- */
- 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)) {
- return ioc.get(className);
- }
- Object obj = null;
- try {
- obj = Class.forName(className).newInstance();
- ioc.put(className, obj);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return obj;
- }
- }
- }
1.《Sring 5 核心原理与30个类手写实战》,谭勇徳,中国公信出版社,2019.
2.极客时间课程《Java性能调优实战》,刘超,2019.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。