设计模式(4) ——- 单例模式
这几条比较忙,都没时间码代码了。 俗话说:三天不打上房揭瓦。今天必须写一写
继续设计模式的学习,本篇简单介绍一下单例模式
概述
单例模式的由来(作用)
很多时候,我们需要在应用中保存一个唯一的实例。比如,后台服务程序需要一个全局的计数器。通俗的讲,单例模式实现的就是使一个类的实例对象唯一,实例对象都指向的同一个地址。
单例模式的实现
很明显,要想保证一个类的实例唯一就必须把该类的构造方法私有化,以使在外部不能访问,从而为实现单例提供基本保证。接下来,三种实现方式:懒汉式、饿汉式、登记式。这里主要使用的是前两种方法。废话少说,直接上代码。
懒汉式
package 单例模式; /** * 懒汉式 * @author by kissx on 2017/1/30. */ public class LazySingleton { private static LazySingleton singleton = null; private LazySingleton(){} /* //实现同步方法一 public static LazySingleton getInstance(){ synchronized (LazySingleton.class){ if (singleton == null) singleton = new LazySingleton(); return singleton; } } //*/ //实现同步方法二 public synchronized LazySingleton getInstance(){ if (singleton == null) singleton = new LazySingleton(); return singleton; } }
说明:synchronized 关键字是用来实现同步的
饿汉式
package 单例模式; /** * 饿汉式 * @author by kissx on 2017/1/30. */ public class EagerSingleton { private static final EagerSingleton singleton = new EagerSingleton(); private EagerSingleton() {} public static EagerSingleton getInstance(){ return singleton; } }
说明:饿汉式与懒汉式的区别在于:它们加载 完成 单例类的时机不同,懒汉式是在需要的时候(即获取此类的时候)才加载 完成,饿汉式是在一开始就加载 完成。但是,懒汉式需要考虑线程安全,而饿汉式在根本上就能保证线程安全。
登记式(利用缓存技术)
package 单例模式; import java.util.HashMap; import java.util.Map; /** * 登记式 * 此处的登记式单例只是针对该类,而需要针对多个类时可以使用反射完成 * @author by kissx on 2017/1/30. */ public class RegSingleton { private static final String KEY = "ONE"; private static Map<String,RegSingleton> map = new HashMap<>(); static{ RegSingleton regSingleton = new RegSingleton(); map.put(KEY,regSingleton); } private RegSingleton(){} public synchronized static RegSingleton getInstance(String key){ map.computeIfAbsent(key, k -> new RegSingleton()); return map.get(key); } public synchronized static RegSingleton getInstance(){ return getInstance(KEY); } }
说明:不常用,了解一下就可以,感兴趣的可以查一下 java 的 缓存技术。
举例说明
案例说明
在 Java 中经常需要读取配置文件的内容,在很多应用项目中都有与应用相关的配置文件,这些配置文件多是由项目开发人员自定义的,在里面定义一些应用需要的参数数据。在实际的项目中,这种配置文件采用 XML 格式,当然也有采用 properties 文件。这里我们采用 properties 文件。如何创建一个类来实现读取配置文件。
代码
package 单例模式; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** * 单例模式的优化实现 * @author by kissx on 2017/1/30. */ public class AppConfig { private String parameterA; private String parameterB; private static class AppConfigHolder{ private static AppConfig appConfig = new AppConfig(); } private AppConfig(){ Properties properties = new Properties(); try(InputStream in = AppConfig.class.getResourceAsStream("AppConfig.properties")){ properties.load(in); parameterA = properties.getProperty("paramA"); parameterB = properties.getProperty("paramB"); } catch (IOException e) { e.printStackTrace(); } } public static AppConfig getInstance(){ return AppConfigHolder.appConfig; } public String getParameterA() { return parameterA; } public String getParameterB() { return parameterB; } public static void main(String[] args) { AppConfig appConfig = AppConfig.getInstance(); AppConfig appConfig1 = AppConfig.getInstance(); System.out.println("A: " + appConfig.getParameterA()); System.out.println("B: " + appConfig.getParameterB()); System.out.println(appConfig.toString() + " : " + appConfig1.toString()); } }
说明:这里已经创建了一个 AppConfig.properties 配置文件,内容如下
还有这里实现单例模式使用了新方式,具体见下面优化中的方案二。paramA=a paramB=b
单例模式优化
上面只是介绍了实现单例模式的三种基本方法。接下来,我们考虑一下如何精益求精地实现单例模式。
方案一(双重检查加锁)
该方法是对 懒汉式 的优化。在懒汉式里面我们需要每次都进行线程安全检查,每次的线程安全检查都会消耗一部分时间。在这里,我们利用两次检查实例是否为空来减少线程安全检查的次数。这样一来,整个过程中只需要几次同步(线程安全检查),从而起到了优化作用。具体实现如下:
package 单例模式; /** * 懒汉式优化 ---- 双重检查加锁 * 疑问: 这里到底需不需要 volatile 关键字 * @author by kissx on 2017/1/30. */ public class Singleton { private volatile static Singleton singleton; public static Singleton getInstance(){ if (singleton == null) { synchronized (Singleton.class){ if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
说明:正如代码中所述,这里不知道是否必须使用 volatile 关键字。个人认为可以不使用。这样的话该方法还是不错的。如果必须加 volatile 关键字,那么会影响 jvm 对代码的优化,从而影响整个的运行速度。
方案二
这里利用的是 java 内部机制,巧妙的解决了线程安全问题以及加载完成的时间问题。这里利用是类中的静态内部类,具体这里不再描述。实现代码可以借鉴上面的举例说明。
方案三(最佳)
这里利用单元素的枚举类型来实现单例模式。枚举类型实质上相当于功能齐全的类,该处的单元素就是那个唯一的实例。实现代码如下
package 单例模式; /** * 利用枚举实现单例模式,更高效、安全、简洁 * @author by kissx on 2017/1/30. */ public enum EnumSingleton { uniqueInstance; //一个元素表示一个实例 //操作方法 EnumSingleton(){ } }
总结
对于单例模式在实际应用中要学会扩展,比如对于某个类的实例对象的限制不是一个而是有限的几个,这里可以利用单例模式加上缓存技术来实现。
就这些吧,累呀~~~