设计模式学习-单例模式

摘要:
这里提供了单例模式的两个实际实现,它们都是线程安全的。这里推荐第一个实现,它实现了线程安全和延迟加载packagecom.zhaoyangwoo.singleton/***Createdbyjohnon16/4/28*/publicclassSingleton{privateSingleton(){}publicstaticSingletonInstance()}returnSingletonHolder.INSTANCE;}//延迟加载privatestatisticclassSingletonHolder{staticfinalSingletonINSTANCE=newSingleton);}}packagecom。zhaoyangwoo.sington/***由johnon16/4/28创建。**/publicclassSingleton2{/**Hunger style*/privatestaticSingleton2instance=newSingleton1();privateSingleton3(){}publicstaticSingleton2getInstance(){returninstance;}}此外,我不太建议实现双重检查锁定,因为它在JDK1.4之前可能会失败。

1.定义

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

2.类图

设计模式学习-单例模式第1张

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.单例模式详解

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇任意多边形面积的计算win7 安装SQL Server 2005 开发版 图文教程下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java 单例模式

单例模式 : 一个类创建的 任何对象都是同一个只存在一个实例,都只开辟同一块内存空间 单例模式就是不管外部如何创建 都只是创建一个对象 对象操作的也是只个唯一对象 饿汉式: public classSingleton { public static voidmain(String[] args) { Pattern patternO...

iOS开发-单例模式

什么是单例模式?>是开发设计模式(共23种)中的1种>它可以保证在程序运行过程,一个类只有一个实例(一个对象),而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源>使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次) iOS系统中的一些单例UIApplication(应用程序实例) NSNoti...

设计模式(一)单例模式:2-懒汉模式(Lazy)

思想: 相比于饿汉模式,懒汉模式实际中的应用更多,因为在系统中,“被用到时再初始化”是更佳的解决方案。 设计思想与饿汉模式类似,同样是持有一个自身的引用,只是将 new 的动作延迟到 getinstance() 方法中执行。 public final classLazySingleton { private staticLazySingleton...

JavaScript设计模式与开发实践-读书笔记(4)单例模式

单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 1.实现单例模式 要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。 var Singleton = function(name){ this.name = na...

PHP的单例模式

单例模式顾名思义,就是只有一个实例。 作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例, 这个类我们称之为单例类。 单例模式的要点有三个: 一是某个类只能有一个实例; 二是它必须自行创建这个实例; 三是它必须自行向整个系统提供这个实例。 下面我们讨论下为什么要使用PHP单例模式? 多数人都是从单例模式的字面上的意...

从源码中学习设计模式系列——单例模式序/反序列化以及反射攻击的问题(二)

一、前言 这篇文章是学习单例模式的第二篇,之前的文章一下子就给出来看起来很高大上的实现方法,但是这种模式还是存在漏洞的,具体有什么问题,大家可以停顿一会儿,思考一下。好了,不卖关子了,下面我们来看看每种单例模式存在的问题以及解决办法。 二、每种Singleton 模式的演进 模式一 public classLazySingleton {...