文章目录
单列模式
简述:
定义: 确保一个类只有一个实例,并提供一个全局访问点;
类图:
定义 和 类图 参考 《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();
}
}
}
喜欢的请点赞… ?