简介
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个 JVM 中,该对象只有一个实例存在。这样的模式有几个好处:
单例模式的优点:
由于单例模式只生产一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
单例模式可以在系统设置全局的访问点,优化环境共享资源访问,例如可以设计一个单例类,负责所以数据表的映射处理
常见的五种单例模式实现方式 :
主要:
饿汉式(线程安全,调用效率高;但是,不能延迟加载)
懒汉式(线程安全,调用效率不高;但是,可以延迟加载)
其他:
双重检测锁式(由于 JVM 底层内部模型原因,偶尔会出问题,不建议使用)
静态内部类式(线程安全,调用效率高;但是,可以延时加载)
枚举单例(线程安全,调用效率高,不能延时加载)
饿汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 * @ProjectName : * @Package : com.example.gof23.creational_patterns.singleton * @ClassName : SingletonHg * @Description : 饿汉式单例模式(Hungry) * @Author : Mr.Vincent * @CreateDate : 2019 /8 /4 23 :33 * @Version : 1.0 .0 */ public class SingletonHungry { private static SingletonHungry instance = new SingletonHungry(); private SingletonHungry () { } public static SingletonHungry getInstance () { return instance; } }
懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 * @ProjectName : * @Package : com.example.gof23.creational_patterns.singleton * @ClassName : SingletonLazy * @Description : 懒汉式单例模式(Lazy) * @Author : Mr.Vincent * @CreateDate : 2019 /8 /4 23 :44 * @Version : 1.0 .0 */ public class SingletonLazy { private static SingletonLazy instance; private SingletonLazy () { } public static synchronized SingletonLazy getInstance () { if (instance == null ) { instance = new SingletonLazy(); } return instance; } }
双重检测锁式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 * @ProjectName : * @Package : com.example.gof23.creational_patterns.singleton * @ClassName : SingletonDC * @Description : 双重检测锁式单例模式(Double Checked Locking) * @Author : Mr.Vincent * @CreateDate : 2019 /8 /5 0 :14 * @Version : 1.0 .0 */ public class SingletonDC { private volatile static SingletonDC instance; private SingletonDC () { } public SingletonDC getInstance () { if (instance == null ) { synchronized (SingletonDC.class) { if (instance == null ) { instance = new SingletonDC(); } } } return instance; } }
静态内部类式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 * @ProjectName : * @Package : com.example.gof23.creational_patterns.singleton * @ClassName : SingletonSIC * @Description : 静态内部类式单例模式(Static Inner Class) * @Author : Mr.Vincent * @CreateDate : 2019 /8 /5 0 :28 * @Version : 1.0 .0 */ public class SingletonSIC { private static class SingletonClassInstance { private static final SingletonSIC instance = new SingletonSIC(); } private SingletonSIC () { } public static SingletonSIC getInstance () { return SingletonClassInstance.instance; } }
枚举单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 * @ProjectName : * @Package : com.example.gof23.creational_patterns.singleton * @ClassName : SingletonEnum * @Description : 枚举类单例模式(Enum) * @Author : Mr.Vincent * @CreateDate : 2019 /8 /5 0 :40 * @Version : 1.0 .0 */ public enum SingletonEnum { INSTANCE; public void singletonOperation () { } }
测试
测试单例模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Client { public static void main (String[] args) { SingletonHungry hungry1 = SingletonHungry.getInstance(); SingletonHungry hungry2 = SingletonHungry.getInstance(); System.out.println(hungry1); System.out.println(hungry2); SingletonLazy lazy1 = SingletonLazy.getInstance(); SingletonLazy lazy2 = SingletonLazy.getInstance(); System.out.println(lazy1); System.out.println(lazy2); SingletonDC dc1 = SingletonDC.getInstance(); SingletonDC dc2 = SingletonDC.getInstance(); System.out.println(dc1); System.out.println(dc2); SingletonSIC sic1 = SingletonSIC.getInstance(); SingletonSIC sic2 = SingletonSIC.getInstance(); System.out.println(sic1); System.out.println(sic2); SingletonEnum anEnum1 = SingletonEnum.INSTANCE; SingletonEnum anEnum2 = SingletonEnum.INSTANCE; System.out.println(anEnum1==anEnum2); } }
结果为:
1 2 3 4 5 6 7 8 9 饿汉式:com.example .gof23 .creational_patterns .singleton .SingletonHungry@1540 e19d 饿汉式:com.example .gof23 .creational_patterns .singleton .SingletonHungry@1540 e19d 懒汉式:com.example .gof23 .creational_patterns .singleton .SingletonLazy@677327 b6 懒汉式:com.example .gof23 .creational_patterns .singleton .SingletonLazy@677327 b6 双重检测锁:com.example .gof23 .creational_patterns .singleton .SingletonDC@14 ae5a5 双重检测锁:com.example .gof23 .creational_patterns .singleton .SingletonDC@14 ae5a5 静态内部类:com.example .gof23 .creational_patterns .singleton .SingletonSIC@7 f31245a 静态内部类:com.example .gof23 .creational_patterns .singleton .SingletonSIC@7 f31245a 枚举单例:true
测试五种单例模式在多线程环境下的效率
(关注相对值即可,不同的环境下测试值完全不一样)
五种单例模式
时间
饿汉式(SingletonHungry)
26ms
懒汉式(SingletonLazy)
186ms
双重检测锁式(SingletonDC)
40ms
静态内部类式(SingletonSIC)
31ms
枚举单例(SingletonEnum)
37ms
CountDownLatch
同步辅助类,在完全一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
countDown()
:当前线程调用此方法,则计数减一(建议放在 finally 里执行)
await()
:调用此方法会一直阻塞当前线程,直到计时器的值为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ClientTimes { public static void main (String[] args) throws InterruptedException { long start = System.currentTimeMillis(); int threadNum = 10 ; final CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (int i = 0 ; i < threadNum; i++) { new Thread(new Runnable() { public void run () { for (int i = 0 ; i < 1000000 ; i++) { SingletonHungry hungry = SingletonHungry.getInstance(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long end = System.currentTimeMillis(); System.out.println("总耗时:" + (end - start)); } }
测试结果如上表格
问题(拓展)
反射可以破解上面几种(不包含枚举式)实现反式(可以在构造方法中手动抛出异常控制)
反序列化可以破解上面几种(不包含枚举式)实现方式(可以通过定义 readResolver() 防止获得不同的对象)
反序列化时,如果对象所在的类定义了 readResolver(),(实际是一种回调),定义返回那个对象
例:反射破解单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.example.gof23.creational_patterns.singleton.expand;public class SingletonDemo { private static SingletonDemo instance = new SingletonDemo(); private SingletonDemo () { } public static SingletonDemo getInstance () { return instance; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test_reflect { public static void main (String[] args) throws Exception { Class<SingletonDemo> clazz = (Class<SingletonDemo>) Class.forName("com.example.gof23.creational_patterns.singleton.expand.SingletonDemo" ); Constructor<SingletonDemo> c = clazz.getDeclaredConstructor(null ); c.setAccessible(true ); SingletonDemo sd1 = c.newInstance(); SingletonDemo sd2 = c.newInstance(); System.out.println(sd1); System.out.println(sd2); } }
结果为:
1 2 com.example .gof23 .creational_patterns .singleton .expand .SingletonDemo@1540 e19d com.example .gof23 .creational_patterns .singleton .expand .SingletonDemo@677327 b6
防止反射破解单例模式:在构造方法中手动抛出异常控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.example.gof23.creational_patterns.singleton.expand;public class SingletonDemo { private static SingletonDemo instance = new SingletonDemo(); private SingletonDemo () { if (instance != null ) { throw new RuntimeException(); } } public static SingletonDemo getInstance () { return instance; } }
再运行的结果为:
1 2 com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4 com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4
例:反序列化破解单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.example.gof23.creational_patterns.singleton.expand;import java.io.Serializable;public class SingletonDemo implements Serializable { private static SingletonDemo instance = new SingletonDemo(); private SingletonDemo () { } public static SingletonDemo getInstance () { return instance; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Test_serializable { public static void main (String[] args) throws Exception { SingletonDemo instance1 = SingletonDemo.getInstance(); FileOutputStream fos = new FileOutputStream("e:/a.txt" ); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(instance1); FileInputStream fis = new FileInputStream("e:/a.txt" ); ObjectInputStream ois = new ObjectInputStream(fis); SingletonDemo instance2 = (SingletonDemo) ois.readObject(); System.out.println(instance1); System.out.println(instance2); } }
结果为:
1 2 com.example .gof23 .creational_patterns .singleton .expand .SingletonDemo@135 fbaa4 com.example .gof23 .creational_patterns .singleton .expand .SingletonDemo@58372 a00
防止反序列化破解单例模式:通过定义 readResolver() 防止获得不同的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.example.gof23.creational_patterns.singleton.expand;import java.io.Serializable;public class SingletonDemo implements Serializable { private static SingletonDemo instance = new SingletonDemo(); private SingletonDemo () { if (instance != null ) { throw new RuntimeException(); } } public static SingletonDemo getInstance () { return instance; } private Object readResolve () { return instance; } }
再运行结果为:
1 2 com.example .gof23 .creational_patterns .singleton .expand .SingletonDemo@135 fbaa4 com.example .gof23 .creational_patterns .singleton .expand .SingletonDemo@135 fbaa4
总结:如何用
单例对象占用资源少,不需要延迟加载:
单例对象占用资源大,需要延迟加载:
参考源码:https://github.com/V-Vincen/GOF23
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !