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(); }}