设计模式学习-单例模式

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.单例模式详解
原文地址:https://www.cnblogs.com/zhaoyanghoo/p/5442540.html