网站首页 包含标签 饿汉 的所有文章

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

    面试官问: 不使用锁,如何实现线程安全的单例? 如果不能使用synchronized和lock的话,想要实现单例可以通过饿汉模式、枚举、以及静态内部类的方式实现。 饿汉: 其实都是通过定义静态的成员变量,以保证instance可以在类初始化的时候被实例化。 // 单例模式 // 饿汉式(静态变量) class Singleton { // 1. 构造器私有化 private Singleton() {} // 2. 本类内部创建对象实例 private final static Singleton instance = new Singleton(); // 静态变量 // 3. 提供一个公有的静态方法,返回实例对象 public static Singleton getInstance() { return instance; } } 但是,如果从始至终未使用过这个实例,会造成内存浪费 静态内部类: 这种方式和饿汉方式只有细微差别,只是做法上稍微优雅一点。 // 静态内部类实现 class Singleton { // 构造器私有化 private Singleton() {} // 写一个静态内部类,含一个静态属性Singleton private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } // 提供一个静态的公有方法,直接返回SingleInstance.INSTANCE public 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 }); } } 其实,上面三种方式,都是依赖静态数据在类初始化的过程中被实例化这一机制的。 但是,如果真要较真的话,ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。 也正是因为这样, 除非被重写,这个方法默认在整个装载过程中都是同步的(线程安全的)。 那么,除了上面这三种,还有一种无锁的实现方式,那就是CAS。 public class Singleton { // 使用了 AtomicReference 封装单例对象 private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); 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造成较大的执行开销。 ...

    2023-10-25 200

联系我们

在线咨询:点击这里给我发消息

QQ交流群:KirinBlog

工作日:8:00-23:00,节假日休息

扫码关注