Java面试题:不使用锁如何实现线程安全的单例?

Java面试题:不使用锁如何实现线程安全的单例? 。小编来告诉你更多相关信息 。
Java面试题
为大家介绍的是Java面试题的内容,接下来带大家一起了解 。
面试官问:不使用锁,如何实现线程安全的单例?
如果不能使用synchronized和lock的话,想要实现单例可以通过饿汉模式、枚举、以及静态内部类的方式实现 。

Java面试题:不使用锁如何实现线程安全的单例?

文章插图
饿汉:
其实都是通过定义静态的成员变量,以保证instance可以在类初始化的时候被实例化 。
// 单例模式// 饿汉式(静态变量)class Singleton {// 1. 构造器私有化private Singleton() {}// 2. 本类内部创建对象实例private final static Singleton instance = new Singleton(); // 静态变量// 3. 提供一个公有的静态方法 , 返回实例对象public static Singleton getInstance() {return instance;}}【Java面试题:不使用锁如何实现线程安全的单例?】但是,如果从始至终未使用过这个实例,会造成内存浪费
静态内部类:
这种方式和饿汉方式只有细微差别,只是做法上稍微优雅一点 。
// 静态内部类实现class Singleton {// 构造器私有化private Singleton() {}// 写一个静态内部类,含一个静态属性Singletonprivate static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}// 提供一个静态的公有方法 , 直接返回SingleInstance.INSTANCEpublic static Singleton getInstance() {return SingletonHolder.INSTANCE;}}原理和饿汉一样 。这种方式是Singleton类被装载了 , INSTANCE 对象不一定被初始化 。
因为SingletonHolder类没有被主动使用 , 只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance
枚举:
借助了 JDK1.5 中添加的枚举来实现单例模式 , 不仅避免了多线程同步问题,而且还防止反序列化重新创建新的对象
// 枚举实现单例enum Singleton {INSTANCE; // 属性}其实,如果把枚举类进行反编译,你会发现他也是使用了static final来修饰每一个枚举项 。
final class Singleton extends Enum{public static final Singleton INSTANCE;private static final Singleton $VALUES[];public static Singleton[] values(){return (Singleton[])$VALUES.clone();}public static Singleton valueOf(String name){return (Singleton)Enum.valueOf(cn/itsource/logweb/utils/Singleton, name);}private Singleton(String s, int i){super(s, i);}static{INSTANCE = new Singleton(\"INSTANCE\", 0);$VALUES = (new Singleton[] {INSTANCE});}}其实,上面三种方式 , 都是依赖静态数据在类初始化的过程中被实例化这一机制的 。
Java面试题:不使用锁如何实现线程安全的单例? 。小编来告诉你更多相关信息 。
Java面试题
但是,如果真要较真的话,ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字 。
也正是因为这样,除非被重写 , 这个方法默认在整个装载过程中都是同步的(线程安全的) 。
那么,除了上面这三种,还有一种无锁的实现方式,那就是CAS 。
public class Singleton {// 使用了 AtomicReference 封装单例对象private static final AtomicReference INSTANCE = new AtomicReference();private Singleton() {}public static Singleton getInstance() {while (true) {// 使用 AtomicReference.get 获取Singleton singleton = INSTANCE.get();if (null != singleton) {return singleton;}// 使用 CAS 乐观锁进行非阻塞更新singleton = new Singleton();if (INSTANCE.compareAndSet(null, singleton)) {return singleton;}}}}用CAS的好处在于不需要使用传统的锁机制来保证线程安全 。
但是我们的实现方式中,用了一个while循环一直在进行重试,所以,这种方式有一个比较大的缺点在于,如果忙等待一直执行不成功(一直在死循环中),会对CPU造成较大的执行开销 。
以上是网关于Java面试题 和 不使用锁如何实现线程安全的单例的技术经验小知识,以上内容供大家学习了解!