design_model(1)singleton
1.单例模式:单例模式指的是一个类只会有一个实例,即是instance,java web中Servlet就是单实例多线程的,单实例运用场景很多,例如在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例,单例的好处:节省内存,不需要new出来那么多实例;配合线程同步;一般用于公共资源。
uml关系图:

2.单例模式中设计的主要的问题:效率(同步影响效率),安全(线程安全),是否可延时(资源),是否可以避免反射和反序列化漏洞(枚举获取方式)
3.饿汉式:线程安全,效率高,在类加载时初始化不能延时加载,如果没有调用赵成资源浪费
//饿汉式:线程安全,效率高,在类加载时初始化不能延时加载,如果没有调用赵成资源浪费
public class SingletonTest1 {
private static SingletonTest1 instance = new SingletonTest1();
private SingletonTest1() {
}
public static SingletonTest1 getInstance() {
return instance;
}
}
4.懒汉式:线程安全,因为可能需要并发操作所以效率不高,可以延迟加载避免资源浪费
//懒汉式:线程安全,因为可能需要并发操作所以效率不高,可以延迟加载避免资源浪费
public class SingletonTest2 {
private volatile static SingletonTest2 instance = null;
private SingletonTest2() {
}
public static SingletonTest2 getInstance() {
if (instance == null) {
synchronized (SingletonTest2.class) {
if (instance == null) {
return instance = new SingletonTest2();
}
}
}
return instance;
}
}
5.静态内部类获取:线程安全,效率高,可以延时加载
//静态内部类获取方式:线程安全,效率高,可以延时加载
public class SingletonTest3 {
private SingletonTest3() {
}
private static class createInstance {//类似静态方法
private static SingletonTest3 instance = new SingletonTest3();
static {//没有调用静态类不会输出
System.out.println(1111);
}
}
public static SingletonTest3 getInstance() {
return SingletonTest3.createInstance.instance;
}
}
6.枚举方式获取
//枚举的方式获取:线程安全,效率高,不可以延时加载,可以避免方式和反序列化的路漏洞较为安全
public enum SingletonTest4 {
instance;
public void test(){
}
}
7.反射破解单例模式(非枚举)
public class SingletonReflectTest {
public static void main(String[] args) throws Exception {
//除了枚举,其他类似
Class<?> singletonClass = Class.forName("cn.singleton.test.SingletonTest1");
Constructor<?> declaredConstructor = singletonClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonTest1 instance = (SingletonTest1)declaredConstructor.newInstance();
System.out.println(instance);
}
}
防止破解方法(只能是饿汉式,对其余两种这种方法不行,初始化得到的对象和反射得到的对象不是同一个对象,假如先反射的话,就会有问题)
private SingletonTest1() {
if (instance != null) {
throw new RuntimeException();
}
}
8.反序列化破解//前提是实现了Serializable接口
public class SingletonSerializable {
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
SingletonTest1 instance = SingletonTest1.getInstance();
// jdk8新特性,能序列化需要实现Serializable接口
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.txt"))) {
objectOutputStream.writeObject(instance);
System.out.println(instance);//cn.singleton.test.SingletonTest1@55f96302
} catch (Exception e) {
e.printStackTrace();
}
Object readObject;
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.txt"))) {
readObject = objectInputStream.readObject();
System.out.println(readObject);//cn.singleton.test.SingletonTest1@3d4eac69
} catch (Exception e) {
e.printStackTrace();
}
}
}
防止反序列化方法
在序列化的方法中添加如下代码(重写readObject()方法)
public SingletonTest1 readObject() {
return instance;
}
9.上述几种单例模式的效率
public class SingletonThread {
public static void test() {
// instant jdk 1.8新特性
CountDownLatch countDownLatch = new CountDownLatch(10);
long start = Instant.now().toEpochMilli();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {
// SingletonTest1 47
// SingletonTest2 51
// SingletonTest3 47
// SingletonTest3 48
SingletonTest1 instance2 = SingletonTest1.getInstance();
// 枚举需要接受返回值否者报错
// SingletonTest4 instance = SingletonTest4.instance;
}
countDownLatch.countDown();
}
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = Instant.now().toEpochMilli();
System.out.println(end - start);
}
public static void main(String[] args) {
SingletonThread.test();
}
}

浙公网安备 33010602011771号