GoF 23 种设计模式之单例模式

用户头像
无心水
关注
发布于: 2020 年 06 月 23 日
GoF 23种设计模式之单例模式

概念



单例模式是设计模式中使用最为普遍的模式之一。它是一种对象创建模式,用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。



在Java语言中,这样的行为能带来两大好处。

1\. 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于一些重量级的对象而言,是非常可观的一笔系统开销。

2\. 由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。



角色



单例模式的参与者非常简单,只有单例类和使用者两个。

1\. 单例类,提供单例的工程,返回单例类。

2\. 使用者,获取并使用单例类。



基本结构



单例模式的核心在于通过一个接口返回唯一的对象实例。





饿汉模式



饿汉模式的单例,在JVM加载单例类时,单例对象就会被建立。如果此时,这个单例类在系统中还扮演者其他角色,那么在任何使用这个单例类的地方都会初始化这个单例变量,而不管是否会被用到。



//饿汉模式
public class HungrySingleton {
//利用静态变量来记录singleton的唯一实例
private final static HungrySingleton uniqueInstance=new HungrySingleton();

/** * 构造器私有化,只有singleton内部才可以调用 * 确保不会被其他代码实例化 */
private HungrySingleton(){
System.out.println("HungrySingleton is create");
}

public static HungrySingleton getInstance(){
return uniqueInstance;
}
//其他角色
public static void createString(){
System.out.println("createString in HungrySingleton");
}

public static void main(String[] args) {
HungrySingleton.createString();
}
}



运行结果:

`HungrySingleton is create

createString in HungrySingleton`



懒汉模式



懒汉模式,实现延迟加载,确保系统启动时没有额外的负载,提高系统在相关函数调用时的反应速度。



线程不安全的懒汉



//线程不安全的懒汉模式
public class NoSafeSingleton {
//利用静态变量来记录singleton的唯一实例
private static NoSafeSingleton uniqueInstance;

/** * 构造器私有化,只有singleton内部才可以调用 * 确保不会被其他代码实例化 */
private NoSafeSingleton(){
}

//线程不安全
//第一个线程进来判断 uniqueInstance == null,还没有new 出实例的时候 。
// 这个时候第二个线程也进来了,判断的uniqueInstance 也是 null,然后也会 new 出实例的
public static NoSafeSingleton getInstance(){
if (null==uniqueInstance){
uniqueInstance=new NoSafeSingleton();
}
return uniqueInstance;
}
}



线程安全的懒汉



为了使用延迟加载引入的同步关键字反而降低了系统性能。



//线程安全的懒汉模式-实现延迟加载
public class SyncLazySingleton {
private SyncLazySingleton() {
System.out.println("SyncLazySingleton is create");
}

//instance初始值赋予null,确保系统启动时没有额外的负载
private static SyncLazySingleton instance = null;

//使用同步关键字,防止多个实例被创建(多线程环境),synchronized保证线程安全
public static synchronized SyncLazySingleton getInstance() {
if (instance == null) {
instance = new SyncLazySingleton();
}
return instance;
}

//synchronized保证线程安全,降低了系统性能
public static void main(String[] args) {
new Thread(){
public void run(){
long bg = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
// HungrySingleton.getInstance();
SyncLazySingleton.getInstance();
}
System.out.println("spend:" + (System.currentTimeMillis() - bg)+"ms");
}
}.start();
}
}



`HungrySingleton is create

spend:5ms`



`SyncLazySingleton is create

spend:22ms`



内部类实现单例



使用内部类的方式实现单例,既可以做到延迟加载,也不必使用同步关键字,是一种比较完善的实现。



//安全的懒汉模式-使用内部类实现单例 ,实现延迟加载
public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is create");
}

//JVM加载 StaticSingleton时不会初始化内部类SingletonHolder
//当方法getInstance()被调用时,才会加载getInstance()
private static class SingletonHolder{
private static StaticSingleton instance=new StaticSingleton();
}

public static StaticSingleton getInstance(){
return SingletonHolder.instance;
}

public static void main(String[] args) {
StaticSingleton instance=StaticSingleton.getInstance();
System.out.println(instance);
}
}



单例序列化



//序列化和反序列化可能会破坏单例
public class SerSingleton implements Serializable {

//私有构造器阻止被实例化
private SerSingleton() {
}

private static class SingletonHolder {
private static final SerSingleton instance = new SerSingleton();
}

public static SerSingleton getInstance() {
return SingletonHolder.instance;
}

//序列化和反序列化可能会破坏单例
//readResolve method to preserve singleton property
//阻止生成新的实例,总是返回当前对象
private Object readResolve(){
//Return the true one SerSingleton and let garbage collector
// take care of the SerSingleton impersonator.
return SingletonHolder.instance;
}

}



最简洁的单例



//最简洁的单例模式
//无偿地提供了序列化机制,绝对防止多次实例化
public enum SingletonEnum {
INSTANCE;

public static void main(String[] args) {
SingletonEnum singletonEnum = SingletonEnum.INSTANCE;
SingletonEnum singletonEnum1 = SingletonEnum.INSTANCE;

System.out.println(singletonEnum == singletonEnum1);
System.out.println(singletonEnum.getClass());
}
}



`true

class org.wuxinshui.boosters.designPatterns.singleton.SingletonEnu`



破坏单例



通过反射机制,强行调用单例类的构造函数,生成多个单例,破坏单例模式。



StaticSingleton中添加计数器。



public static final AtomicInteger counter=new AtomicInteger();
private StaticSingleton(){
counter.incrementAndGet();
System.out.println("The "+(counter.get())+" StaticSingleton instance is created");
}



//利用反射破坏单例模式
public class ReflectSingleton {
public static void main(String[] args) throws Exception {
StaticSingleton singleton = StaticSingleton.getInstance();

Constructor constructor=singleton.getClass().getDeclaredConstructor();

//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
// 值为 false 则指示反射的对象应该实施 Java 语言访问检查。
constructor.setAccessible(true);

StaticSingleton singleton2= (StaticSingleton) constructor.newInstance();
StaticSingleton singleton3= (StaticSingleton) constructor.newInstance();

System.out.println("singleton2==singleton3 :"+(singleton2==singleton3));
}
}



运行结果:



The 1 StaticSingleton instance is created
The 2 StaticSingleton instance is created
The 3 StaticSingleton instance is created
singleton2==singleton3 :false



发布于: 2020 年 06 月 23 日 阅读数: 73
用户头像

无心水

关注

路漫漫其修远兮 2018.08.16 加入

熟悉Java,略懂Python

评论

发布
暂无评论
GoF 23种设计模式之单例模式