644 字
3 分钟
面试鸭-单例模式有哪几种实现?如何保证线程安全?
单例模式有哪几种实现?如何保证线程安全?
主要有5种(java),包括:饿汉式、懒汉式、双重检查锁定、静态内部类、枚举单例。
- 饿汉式:在类加载时就创建实例,线程安全,但是如果实例没有被使用就会白白浪费资源。
- 懒汉式:在实例第一次被访问的时候创建,节约资源,但是要考虑线程安全问题。
- 双重检查锁定:是懒汉式的优化版,使用双重检查来加锁,首先判断是否存在实例,若不存在则加锁创建,若存在则直接返回。这样即保证了线程安全,又不用每次获取实例时都加锁。要加上volatile关键字来防止指令重排。在JDK1.5之前有缺陷,那时候的volatile关键字不能完全禁止指令重排。
- 静态内部类:依靠JVM类加载机制来保证线程安全,在类里写一个静态内部类,getInstance()方法返回这个静态内部类中的实例,这样就很简单的实现了线程安全的单例模式。
- 枚举单例:同样依靠JVM类加载机制来保证线程安全,因为枚举类中,每一个枚举项本身就是一个单例,因此可以直接创建枚举类,然后在内部写方法,将其作为一个需要的实例去使用。并且JVM内部禁止了使用反射去创建枚举对象,因此可以防止反射攻击。并且反序列化也能防止,Java对枚举对象的序列化和反序列化也有专门的机制,序列化只序列化枚举的名字,不记录内部属性值,反序列化时,会根据名字从JVM中缓存好的枚举常量的Map里查找并返回现有实例。
防止反射入侵的方式: 除了枚举单例因为java实现可以天然的防止反射攻击,其他的需要用别的方式来解决。 可以在构造函数添加判断,若实例已存在则抛出异常。
序列化安全: 防止反射入侵以外,还需要考虑序列化和反序列化的安全性问题。序列化之后进行反序列化,默认情况下会出现一个新的实例,两者地址不同。 可以通过重写readResolve()方法,直接返回实例,这样子可以保证jvm中只有一个实例存在。
问题
面试鸭-单例模式有哪几种实现?如何保证线程安全?
http://www.shineacz.top/posts/面试鸭-单例模式有哪几种实现如何保证线程安全/