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

    1.定义

    一个类有且仅有一个实例,并且自行实例化向整个系统提供

    2.类图

    3.代码示例

    网上最多有8中实现方式,其中包括了很多非线程安全的实现。我觉得没有必要。这里提供单例模式的两种实用实现,均为线程安全,这里推荐第一种实现,即实现了线程安全,又实现了懒加载

    package com.zhaoyangwoo.singleton;
    
    /**
     * Created by john on 16/4/28.
     */
    public class Singleton {

       private Singleton() { } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } //通过内部类机制 实现懒加载 private static class SingletonHolder { static final Singleton INSTANCE = new Singleton(); } }
    package com.zhaoyangwoo.singleton;
    
    /**
     * Created by john on 16/4/28.
     *
     */
    public class Singleton2 {/**
         * 饿汉式
         */
        private static Singleton2 instance = new Singleton2();
    
        private Singleton2() {
        }
    
        public static Singleton2 getInstance() {
            return instance;
        }
    
    }

    另外这里特别提到一下,对于双重检查锁定的实现,我不是很推荐,因为其在JDK1.4版本前是有失败的可能性的。

    原因在于:初始化Singleton和将对象地址写到instance字段的顺序是不确定的。在某个线程new Singleton()时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。此时若另外一个线程来调用getInstance,取到的就是状态不正确的对象。自JDK1.5后对volatile关键字描述的字段,JVM是不会对其读写进行重排序优化,问题才得以解决。

    package com.zhaoyangwoo.singleton;
    
    /**
     * Created by john on 16/4/28.
     * 双重检查锁定
     */
    public class Singleton3 {
    
        private static volatile Singleton3 instance = null;
    
        private Singleton3() {
        }
    
        public static Singleton3 getInstance() {
            if (instance == null) {
                synchronized (Singleton3.class) {
                    if (instance == null) {
                        instance = new Singleton3();
                    }
                }
            }
            return instance;
        }
    } 

    4.应用场景举例

    • 日志管理应用和配置文件的读写
    • 创建一个对象的消耗很大,例如访问IO,数据库等

    5.JDK源码中的模式实现

    java.lang.Runtime对象的创建,从源码可以看出这里是用了饿汉式的实现方式。同样使用到单例模式的有java.lang.System等

    package java.lang;
    
    public class Runtime {
        private static Runtime currentRuntime = new Runtime();
    
        public static Runtime getRuntime() {
            return currentRuntime;
        }
    
        /**
         * Don't let anyone else instantiate this class
         */
        private Runtime() {
        }
    
        ...
    }

     另外值得注意一点的是Calendar日历类,Calendar.getInstance() 看起来好像是获取日历的单例对象,其实不然

    //getInstance
    public static Calendar getInstance()
        {
            return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
        }
    
    private static Calendar createCalendar(TimeZone zone,
                                               Locale aLocale)
        {
            CalendarProvider provider =
                LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                     .getCalendarProvider();
            if (provider != null) {
                try {
                    return provider.getInstance(zone, aLocale);
    //它的实现,其实是产生了一个新的对象,并不符合单例的定义
    /*
    * public Calendar getInstance(TimeZone var1, Locale var2) {
    *        return (new Builder()).setLocale(var2).setTimeZone(var1).setInstant(System.cu*rrentTimeMillis()).build();
    * }
    */
                } catch (IllegalArgumentException iae) {
                    // fall back to the default instantiation
                }
            }
    
            Calendar cal = null;
    
          ...
    if (cal == null) { if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") { cal = new BuddhistCalendar(zone, aLocale); } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") { cal = new JapaneseImperialCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } } return cal; }

    6.思考

    思考如下两个问题

    • 单例模式状态修改 

    一般单例模式是不保存或者说不改变实例的状态,如果单例模式涉及到状态修改/保存,那么其方法调用需要考虑到多线程同步问题

    • 单例模式vs静态类

        工作中需要读取一个配置property文件,之前的实现是这样的:

    public class PropertiesGetter {
        
        private static Properties privateProperties;
    
        static {
                privateProperties.load(new FileInputStream(System.getProperty("configHome/web.properties"));
        }
    
        public static String getStringProperty(String propertyNameEnum) {
            return privateProperties.containsKey(propertyNameEnum)?privateProperties.getProperty(propertyNameEnum):null;
        }
        //删减无关代码
        ...
        
    }

    //调用是这样的
    PropertiesGetter.getStringProperty("XXXX");

      现在改成了单例模式

    public class PropertiesGetter {
    
        private PropertiesGetter() {
        }
    
        private static final PropertiesGetter instance = new PropertiesGetter();
    
        public static PropertiesGetter getInstance() {
            return instance;
        }
    
        private static Properties privateProperties = new Properties();
    
        static {
            privateProperties.load(new FileInputStream(System.getProperty("configHome/web.properties"));
        }
    
        public String getStringProperty(PropertiesNameEnum propertyNameEnum) {
            return privateProperties.containsKey(propertyNameEnum)?privateProperties.getProperty(propertyNameEnum):null;
        }
    
        //无关代码
        ...
    }
    
    //调用
    PropertiesGetter.getInstance().getStringProperty("XXXX");

     就单单这个例子而言,我也说不出两种实现方式孰优孰劣?看官有想法请指教

    7.参考

    1.再议单例模式和静态类 
    2.单例模式的7种写法
    3.单例模式详解
  • 相关阅读:
    又玩起了“数独”
    WebService应用:音乐站图片上传
    大家都来DIY自己的Blog啦
    CSS导圆角,不过这个代码没有怎么看懂,与一般的HTML是不同
    网站PR值
    CommunityServer2.0何去何从?
    网络最经典命令行
    炎热八月,小心"落雪"
    Topology activation failed. Each partition must have at least one index component from the previous topology in the new topology, in the same host.
    SharePoint 2013服务器场设计的一些链接
  • 原文地址:https://www.cnblogs.com/zhaoyanghoo/p/5442540.html
Copyright © 2011-2022 走看看