
目前代码已经上传到GitHub,里面有设计模式系列的整个代码
GitHub 设计模式地址
1、什么是单例模式?
确保一个类只有一个实例,而且自行实例化并向整个系统提供一个全局访问点。
2、单例模式的优点:
在内存中只有一个对象,节省内存空间。
避免频繁的创建销毁对象,可以提高性能。
避免对共享资源的多重占用。
可以全局访问。
3、单例模式的常见的写法
饿汉、懒汉、懒汉安全、DCL、枚举、容器、静态内部类
饿汉模式
/**
* Created by Snail on 12/6/2017 11:02 AM
* Contact with slowsnail0223@gmail.com
* 饿汉模式
*/
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
线程安全的,但是声明的时候已经初始化了,对于一些不是很常用的,会消耗一定的资源。
懒汉模式
/**
* Created by Snail on 12/6/2017 10:54 AM
* Contact with slowsnail0223@gmail.com
* 懒汉模式
*/
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
懒汉模式 只有使用时才会实例化,在一定程度上节约了资源,缺点是第一次加载时需要及时进行实例化,反应稍慢,线程不安全
饿汉安全模式
/**
* Created by Snail on 12/6/2017 10:56 AM
* Contact with slowsnail0223@gmail.com
* 懒汉模式 方法同步锁
*/
public class LazySafetySingleton {
private static LazySafetySingleton instance;
private LazySafetySingleton() {
}
public static synchronized LazySafetySingleton getInstance() {
if (instance == null) {
instance = new LazySafetySingleton();
}
return instance;
}
}
getInstance()添加了synchronized关键字,即使instance已经初始化了,那么下次在访问的时候还是会进行同步,这样会消耗不必要的资源。
DCL模式
/**
* Created by Snail on 12/6/2017 11:01 AM
* Contact with slowsnail0223@gmail.com
* Double Check Lock
*/
public class DclSingleton {
private volatile static DclSingleton instance = null;
private DclSingleton() {
}
public static DclSingleton getInstance() {
if (instance == null) {
synchronized ((DclSingleton.class)) {
if (instance == null) {
instance = new DclSingleton();
}
}
}
return instance;
}
}
DCL方式实现单例的优点是既能在需要时才初始化单例,又能够保证线程安全,且单例对象在初始化后调用getInstance()不进行同步锁,但是也有需要注意的地方。
instance = new DclSingleton()编译成汇编指令的时候,大概做了三件事:
1、给DclSingleton 的实例分配内存
2、调用DclSingleton()的构造函数,初始化成员字段
3、将instance对象指向分配的内存空间
但是由于Java编译器允许处理器乱序执行,上面的2和3的顺序是无法保证的,可能是1-2-3,也可能是1-3-2,如果是后者,并且在3执行完毕,2执行之前,被切换到线程B上,这时候instance在A内已经制定过了第三点,instance已经是非空了,所有线程B直接取走instance,再使用时就会出错,这就是DCL失效问题,而且这种难以跟踪和重现。
在JDK1.5之后,SUN官方注意到这种问题,调整了JVM,具体化了volatile关键字, private volatile static DclSingleton instance = null;就可以保证instance对象每次都是从主内存中读取。
DCL的有点:资源利用率高,第一次执行getInstance()时单例对象才会被初始化,效率高。缺点:
第一次加载的时候反应稍慢,也由于Java内存模型的原因偶尔会失败,在高并发的环境下有一定的缺陷,DCL是使用最多的单例模式实现方式。
枚举模式
/**
* Created by Snail on 12/6/2017 11:04 AM
* Contact with slowsnail0223@gmail.com
* 枚举模式
*/
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
}
}
写法简单是枚举单例的最大的优点,枚举在Java中与普通的类是一样的,不仅能够有字段还能够有自己的方法,最重要的事默认枚举实例创建的单例是线程安全的,上面几种通过反序列化会重现重新穿件对象的情况,通过反序列化将一个单例的实例对象写入到磁盘,然后再堵回来,从而有效的获得一个实例。
静态内部类模式
/**
* Created by Snail on 12/6/2017 11:10 AM
* Contact with slowsnail0223@gmail.com
* 静态内部类
*/
public class StaticInnerSingleton {
private StaticInnerSingleton() {
}
public static StaticInnerSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}
}
当第一次加载StaticInnerSingleton 类时并不会初始化instance,只有第一次调用StaticInnerSingleton 的getInstance()方法才会导致instance被初始化,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,是最推荐的单例实现模式。
容器单例模式
/**
* Created by Snail on 12/6/2017 11:20 AM
* Contact with slowsnail0223@gmail.com
* 容器单例模式
*/
public class MapSingleton {
private static Map<String, Object> map = new HashMap<>();
private MapSingleton() {
}
public static void registerSingleton(String key, Object instance) {
if (!map.containsKey(key)) {
map.put(key, instance);
}
}
public static Object getSingleton(String key) {
return map.get(key);
}
}
在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象,这种方式可以方便的管理多种类型的单例,并且可以使用时通过统一的接口进行获取操作,降低了用户的成本,也对用户隐藏了具体实现,降低了耦合度。
Kotlin中的单例模式
/**
* Created by Snail on 12/15/2017 1:18 PM
* Contact with slowsnail0223@gmail.com
*/
class KotlinSingleton {
public var value: KotlinSingleton? = null
private object mHolder {
val INSTANCE = KotlinSingleton()
}
companion object Factory {
fun getInstance(): KotlinSingleton {
return mHolder.INSTANCE
}
}
}
不管是哪种方式实现单例模式,它们的核心原理就是将构造函数私有化,并且通过静态方法获取唯一一个实例,在这个获取的过程中必须保证线程安全,防止反序列化导致重新生成实例对象。
网友评论