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.单例模式详解
  • 相关阅读:
    POJ 2955 Brackets 区间DP
    POJ 3311 Hie with the Pie 最短路+状压DP
    POJ 3615 Cow Hurdles(最短路径flyod)
    hdu 3790 最短路径dijkstra(多重权值)
    poj 3254 Corn Fields 状压DP
    状压DP
    poj2411 Mondriaan's Dream 状压DP
    M: Mysterious Conch 字符串哈希
    哈希(hash)理解
    域渗透:GPP(Group Policy Preferences)漏洞
  • 原文地址:https://www.cnblogs.com/zhaoyanghoo/p/5442540.html
Copyright © 2011-2022 走看看