zoukankan      html  css  js  c++  java
  • 设计模式的学习和部分应用源码分析(单例模式)

    首先是饿汉式(非延迟加载单例类)

    public class HungrySinglePatterns {
    
        /**
         * 类进行初始化的时候,就立即加载这个对象。没有延时加载的优势。加载类时,线程是安全的。
         */
        private static HungrySinglePatterns instance = new HungrySinglePatterns();
    
        private HungrySinglePatterns() {	}
    
        /**
         * 方法没有同步,调用率高
         */
        public static HungrySinglePatterns getInstance() {
            return instance;
        }
    }
    

    应用:待补充

    2.懒汉式(同步延迟加载)

    public class LazyLoadSinglePatterns {
        private static LazyLoadSinglePatterns instance;
        /**
         * 私有化构造器
         */
        private LazyLoadSinglePatterns() {	}
    
        /**
         * 因为是延时加载所以有可能出现线程同步的问题。所以要加上 同步块。
         * 如果A线程执行 方法。 instance为null的时候, B线程又要执行方法,那么这个时候,两个线程都判断instance为null
         * 就会创建出两个对象。 所以要加上synchronized。 导致调用效率不高。
         * @return
         */
        public static synchronized LazyLoadSinglePatterns getInstance() {
            if (instance == null) {
                return new LazyLoadSinglePatterns();
            }
            return instance;
        }
    }
    

    应用:待补充

    3.双重检测锁

      /**
         * 双重检测锁
         * 懒加载,调用率高。没有同步问题。但是因为jvm底层问题,容易出现问题,不推荐使用
         */
        public class DoubuCheckLockedSinglePatterns {
            private volatile static DoubuCheckLockedSinglePatterns instance = null;
            private DoubuCheckLockedSinglePatterns() {}
        
            public static DoubuCheckLockedSinglePatterns getInstance() {
                if (instance == null) {  // 1. 第一次检查
                    synchronized (DoubuCheckLockedSinglePatterns.class) {  // 2
                        if (instance == null) {   // 3. 第二次检查
                            instance = new DoubuCheckLockedSinglePatterns();  // 4
                        }
                    }
                }
                return instance;
            }
        }
    

    双重检测锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“重排序”,这也是失败的一个主要原因。所以这里要加volatile才能成功。

    我们假设有两个线程 a 和 b 调用 getInstance() 方法,假设 a 先走,一路走到 4 这一步,执行 instance = new Singleton() 这句代码。

    instance = new Singleton() 这句代码首先会申请一段空间,然后将各个属性初始化为零值(0/null),执行构造方法中的属性赋值[1],将这个对象的引用赋值给 instance[2]。在这个过程中,[1] 和 [2] 可能会发生重排序。

    此时,线程 b 刚刚进来执行到 1(看上面的代码块),就有可能会看到 instance 不为 null,然后线程 b 也就不会等待监视器锁,而是直接返回 instance。问题是这个 instance 可能还没执行完构造方法(线程 a 此时还在 4 这一步),所以线程 b 拿到的 instance 是不完整的,它里面的属性值可能是初始化的零值(0/false/null),而不是线程 a 在构造方法中指定的值。

    应用:

    /**
         * Return the (raw) singleton object registered under the given name.
         * <p>Checks already instantiated singletons and also allows for an early
         * reference to a currently created singleton (resolving a circular reference).
         * @param beanName the name of the bean to look for
         * @param allowEarlyReference whether early references should be created or not
         * @return the registered singleton object, or {@code null} if none found
         */
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    

    Spring的依赖注入(包括lazy-init方式)都是发生在 AbstractBeanFactory 的 getBean 里。 getBean 的 doGetBean 方法调用 getSingleton 进行bean的创建。lazy-init方式(lazy-init=“true”),在用户向容器第一次索要bean时进行调用;非lazy-init方式(lazy-init=“false”),在容器初始化时候进行调用。
    从上面代码可以看到,spring依赖注入时,使用了双重检测锁的单例模式。也许是现在的jvm不会有双重检测锁失败的问题了?并没有查到相关资料,希望有大佬补充。

    4.静态内部类

    /**
     * 静态内部类,实现懒加载单例模式
     * 线程安全,懒加载,并且实现了延时加载。调用效率高
     */
    public class StaticInteriorSinglePatterns {
    
        private static class SingletonClassInstance{
            private static final StaticInteriorSinglePatterns instance = new StaticInteriorSinglePatterns();
        }
        
        private StaticInteriorSinglePatterns() {}
    
        public static StaticInteriorSinglePatterns getInstance() {
            return StaticInteriorSinglePatterns.SingletonClassInstance.instance;
        }
    }
    

    5.枚举类

    这里我用了一个获取数据库连接的例子

    public enum DataSourceEnum {
        /**
         * mysql的Connection
         */
        DATASOURCE();
    
        private Connection connection = null;
        private DataSourceEnum() {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                String url="jdbc:mysql://localhost/youDatabase?useUnicode=true&characterEncoding=utf-8";
                String user="root";
                String password="root";
                connection = DriverManager.getConnection(url,user,password);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public Connection getConnection() {
            return connection;
        }
    }
    
  • 相关阅读:
    docker介绍与安装
    HTML5之Notification简单使用
    移动端实现复制内容至剪贴板
    flex基本概念
    nodejs建立websocket通信
    使用FileReader实现前端预览所选图片
    去除字符串中的空格
    用swing做一个简单的正则验证工具
    使用命令行生成jar包
    C#语言 语句
  • 原文地址:https://www.cnblogs.com/fatmanhappycode/p/12231202.html
Copyright © 2011-2022 走看看