设计模式-单例模式 JAVA语言实现 (Head First 设计模式)

单列模式

简述:

定义: 确保一个类只有一个实例,并提供一个全局访问点;

类图:
在这里插入图片描述

定义 和 类图 参考 《Head First 设计模式》

一、 懒汉单列 (延迟实例化)

1. 非线程安全的单列模式 (非同步)

@SuppressWarnings("serial")
public class Singleton implements Serializable {

	/**
	 * 静态的实例变量
	 */
	private static Singleton instance;
	
	/**
	 * 私有的构造方法
	 */
	private Singleton() {
		
	}
	
	public static Singleton instance() {
		
		if (instance == null) {
			instance = new Singleton();
		}
		
		return instance;
	}
}

测试代码
在单线程下的获取实例

public class SingletonTest {
	@Test
	public void test() {
		System.out.println(Singleton.instance());
		System.out.println(Singleton.instance());
		System.out.println(Singleton.instance());
		System.out.println(Singleton.instance());
	}
}

输出结果

test.Singleton@23e028a9
test.Singleton@23e028a9
test.Singleton@23e028a9
test.Singleton@23e028a9

多线程下获取实例:

	@Test
	public void test2() {
		// 无参无返回值的 lambda 表达式
		Runnable r = ()-> System.out.println(Thread.currentThread().getName() + " : " + Singleton.instance());
		Thread t1 = new Thread(r, "single-1");
		Thread t2 = new Thread(r, "single-2");
		Thread t3 = new Thread(r, "single-3");
		Thread t4 = new Thread(r, "single-4");
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

输出结果:
这里的测试test2() 多执行几次就会出现不一致的实例

single-1 : test.Singleton@1c89e366
single-4 : test.Singleton@41b4086e
single-2 : test.Singleton@41b4086e
single-3 : test.Singleton@41b4086e

2.线程安全的单列模式(同步)

加了同步代码块的单列模式
关键代码块

synchronized(Singleton.class) {
	......
}

第一个版本的同步模式:

	public static synchronized Singleton instance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}

版本二


	public static Singleton instance() {
		synchronized(Singleton.class) {
			if (instance == null) {
				instance = new Singleton();
			}
		}
		return instance;
	}
	

在执行测试代码test2()
实例一致(同步),但是效率下降了…

single-4 : test.Singleton@41b4086e
single-2 : test.Singleton@41b4086e
single-3 : test.Singleton@41b4086e
single-1 : test.Singleton@41b4086e

优化版本一 的同步模式

	public static Singleton instance() {
		if (instance == null) {
			synchronized(Singleton.class) {
				instance = new Singleton();
			}
		}
		return instance;
	}

测试结果:
这个代码无法同步

single-3 : test.Singleton@41b4086e
single-2 : test.Singleton@273d407a
single-1 : test.Singleton@273d407a
single-4 : test.Singleton@273d407a

优化版本二 的同步模式
这里的代码在 sync 关键字里多加了if 语句, 这是大名鼎鼎的 (双重锁) double-checked locking

	public static Singleton instance() {
		if (instance == null) {
			synchronized(Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}

测试结果:

single-1 : test.Singleton@7e8cd9b3
single-4 : test.Singleton@7e8cd9b3
single-2 : test.Singleton@7e8cd9b3
single-3 : test.Singleton@7e8cd9b3

为什么优化版本一无法同步?
在这里插入图片描述
假若现在有四个线程调用了 instance() 方法, 在还没有调用 new Singleton() 时, instance == null 是成立的;

二、饿汉单列模式 (加载实例化)

这段代码没什么好讲的, 加载时就实例化.不存在线程安全的问题;

@SuppressWarnings("serial")
public class Singleton implements Serializable {

	/**
	 * 静态的实例变量
	 */
	private static Singleton instance = new Singleton();
	
	/**
	 * 私有的构造方法
	 */
	private Singleton() {
		
	}
	
	public static Singleton instance() {
		return instance;
	}
}

三、反序列化 与 反射获取实例 (破坏单例模式)

1 . 序列化 与 反序列化

这段代码摘自 spring 源码 org.springframework.util.SerializationUtils

测试前代码准备
序列化与反序列化工具类

/*--------------------- serialize util ----------------------*/
public abstract class SerializableUtil {
	
	/**
	 * 序列化
	 * @param object
	 * @return
	 */
	public static byte[] serialize(Object object) {
		if (object == null) {
			return null;
		}
		ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
		try {
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(object);
			oos.flush();
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), ex);
		}
		return baos.toByteArray();
	}
	
	/**
	 * 返序列化
	 * @param bytes
	 * @return
	 */
	public static Object deserialize(byte[] bytes) {
		if (bytes == null) {
			return null;
		}
		try {
			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
			return ois.readObject();
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Failed to deserialize object", ex);
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException("Failed to deserialize object type", ex);
		}
	}
}

测试代码:
反序列化获取实例, 破坏单例模式

	@Test
	public void test3() {
		Singleton singleton = Singleton.instance();
		System.out.println(singleton);
		
		byte[] b = SerializableUtil.serialize(singleton);
		Singleton s2 = (Singleton) SerializableUtil.deserialize(b);
		System.out.println(s2);
		System.out.println(s2 == singleton);
	}

测试结果:

test.Singleton@23e028a9
test.Singleton@2758fe70
s2 == singleton : false

防止反序列化破坏代理模式: 在Singleton 类里添加代码
关键代码

    private Object readResolve() {
        return instance();
    }
@SuppressWarnings("serial")
public class Singleton implements Serializable {

	.......省略

    private Object readResolve() {
        return instance();
    }
}

再此测试:

test.Singleton@23e028a9
test.Singleton@23e028a9
s2 == singleton : true

2.反射调用构造方法获取实例 (通过反射破坏单例模式)

测试代码:

	@Test
	public void test4() {
		Singleton singleton = Singleton.instance();
		System.out.println(singleton);
		
		Class<?> clazz = Singleton.class;
		Singleton s2 = null;
		try {
			Constructor<?> constructor = clazz.getDeclaredConstructor();
			constructor.setAccessible(true);
			Object obj = constructor.newInstance();
			if (obj instanceof Singleton) {
				s2 = (Singleton) obj;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(s2);
		System.out.println("s2 == singleton : " + (s2 == singleton));
		
	}

测试结果:出现多个实例

test.Singleton@23e028a9
test.Singleton@3dd4520b
s2 == singleton : false

防止反射破坏单列模式: 在构造方法中添加代码

关键代码

	/**
	 * 私有的构造方法
	 */
	private Singleton() {
		if (instance != null) {
			throw new RuntimeException("not null instance...");
		}
	}

再次测试:
在这里插入图片描述
(完结…)

还有克隆也可以出现多个实例, 但是这是不现实的

	@Test
	public void test5() {
		Singleton singleton = Singleton.instance();
		try {
			Singleton s2 = (Singleton) singleton.clone();
			System.out.println(s2 == singleton);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}

完整代码

测试代码在 JDK 1.8 / eclipse / JUnit 5,环境下运行

package test;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Singleton implements Serializable, Cloneable {

	/**
	 * 静态的实例变量
	 */
	private static Singleton instance;
	
	/**
	 * 私有的构造方法
	 */
	private Singleton() {
		if (instance != null) {
			throw new RuntimeException("not null instance...");
		}
	}
	
	public static Singleton instance() {
		if (instance == null) {
			synchronized(Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
	
    private Object readResolve() {

        return instance();

    }

	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
    
    
}

public class SingletonTest {
	@Test
	public void test() {
		System.out.println(Singleton.instance());
		System.out.println(Singleton.instance());
		System.out.println(Singleton.instance());
		System.out.println(Singleton.instance());
	}
	
	@Test
	public void test2() {
		// 无参无返回值的 lambda 表达式
		Runnable r = ()-> System.out.println(Thread.currentThread().getName() + " : " + Singleton.instance());
		Thread t1 = new Thread(r, "single-1");
		Thread t2 = new Thread(r, "single-2");
		Thread t3 = new Thread(r, "single-3");
		Thread t4 = new Thread(r, "single-4");
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
	
	@Test
	public void test3() {
		Singleton singleton = Singleton.instance();
		System.out.println(singleton);
		
		byte[] b = SerializableUtil.serialize(singleton);
		Singleton s2 = (Singleton) SerializableUtil.deserialize(b);
		System.out.println(s2);
		System.out.println("s2 == singleton : " + (s2 == singleton));
	}
	
	@Test
	public void test4() {
		Singleton singleton = Singleton.instance();
		System.out.println(singleton);
		
		Class<?> clazz = Singleton.class;
		Singleton s2 = null;
		try {
			Constructor<?> constructor = clazz.getDeclaredConstructor();
			constructor.setAccessible(true);
			Object obj = constructor.newInstance();
			if (obj instanceof Singleton) {
				s2 = (Singleton) obj;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(s2);
		System.out.println("s2 == singleton : " + (s2 == singleton));
		
	}
	
	@Test
	public void test5() {
		Singleton singleton = Singleton.instance();
		try {
			Singleton s2 = (Singleton) singleton.clone();
			System.out.println(s2 == singleton);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
}

喜欢的请点赞… ?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值