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();
	}
}
原文地址:https://www.cnblogs.com/gg128/p/9548164.html