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