带参数的 Kotlin 线程安全原生惰性单例

Kotlin thread safe native lazy singleton with parameter

在 java 中,我们可以使用双重检查锁定和 volatile 编写 thead 安全单例:

    public class Singleton {
        private static volatile Singleton instance;

        public static Singleton getInstance(String arg) {
        Singleton localInstance = instance;
        if (localInstance == null) {
            synchronized (Singleton.class) {
                localInstance = instance;
                if (localInstance == null) {
                    instance = localInstance = new Singleton(arg);
                }
            }
        }
        return localInstance;
    }
}

我们如何在 kotlin 中编写它?


关于对象

object A {
    object B {}
    object C {}
    init {
        C.hashCode()
    }
}

我使用 kotlin 反编译器来获得它

public final class A {
   public static final A INSTANCE;

   private A() {
      INSTANCE = (A)this;
      A.C.INSTANCE.hashCode();
   }
   static {
      new A();
   }

   public static final class B {
      public static final A.B INSTANCE;
      private B() {
         INSTANCE = (A.B)this;
      }
      static {
         new A.B();
      }
   }

   public static final class C {
      public static final A.C INSTANCE;
      private C() {
         INSTANCE = (A.C)this;
      }
      static {
         new A.C();
      }
   }
}

所有对象都在 static 块中调用构造函数。基于此,我们可以认为它并不懒惰。

输给正确答案。

    class Singleton {
        companion object {
            val instance: Singleton by lazy(LazyThreadSafetyMode.PUBLICATION) { Singleton() }
        }
    }

反编译:

public static final class Companion {
      // $FF: synthetic field
      private static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Singleton.Companion.class), "instance", "getInstance()Lru/example/project/tech/Singleton;"))};

      @NotNull
      public final Singleton getInstance() {
         Lazy var1 = Singleton.instance$delegate;
         KProperty var3 = $$delegatedProperties[0];
         return (Singleton)var1.getValue();
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }

希望Kotlin开发者以后能做出非反射实现...

Object declaration 正是为了这个目的:

object Singleton {
    //singleton members
}

它是惰性和线程安全的,它会在第一次调用时进行初始化,就像 Java 的静态初始化器一样。

您可以在顶层或在 class 或其他对象内声明 object

有关使用来自 Java 的 object 的更多信息,请参阅


至于参数,如果你想实现完全相同的语义(第一次调用 getInstance 接受它的参数来初始化单例,接下来调用只是 return 实例,丢弃参数),我建议这个构造:

private object SingletonInit { //invisible outside the file
    lateinit var arg0: String
}

object Singleton {
    val arg0: String = SingletonInit.arg0
}

fun Singleton(arg0: String): Singleton { //mimic a constructor, if you want
    synchronized(SingletonInit) {
        SingletonInit.arg0 = arg0
        return Singleton
    }
}

此解决方案的主要缺陷是它需要在单独的文件中定义单例以隐藏 object SingletonInit,并且在初始化之前不能直接引用 Singleton

另外,请参阅 关于为单例提供参数的信息。

Kotlin 与您的 Java 代码等效,但更安全。你的 double lock check is not recommended even for Java. In Java you should use an inner class on the static which is also explained in Initialization-on-demand holder idiom.

但那是 Java。 在 Kotlin 中,只需使用对象(以及可选的惰性委托):

object Singletons {
    val something: OfMyType by lazy() { ... }

    val somethingLazyButLessSo: OtherType = OtherType()
    val moreLazies: FancyType by lazy() { ... }
}

然后您可以访问任何成员变量:

// Singletons is lazy instantiated now, then something is lazy instantiated after.  
val thing = Singletons.something // This is Doubly Lazy!

// this one is already loaded due to previous line
val eager = Singletons.somethingLazyButLessSo

// and Singletons.moreLazies isn't loaded yet until first access...

Kotlin 有意避免了人们对 Java 中单例的混淆。并避免了这种模式的 "wrong versions" -- 其中有很多。相反,它提供了更简单、最安全的单例形式。

考虑到 lazy() 的使用,如果您有其他成员,每个人都会懒惰。由于它们是在传递给 lazy() 的 lambda 中初始化的,因此您可以执行您要求的有关自定义构造函数的操作,以及每个成员 属性.

因此,您延迟加载了 Singletons 对象( 首次访问实例 ),然后延迟加载了 something第一次访问成员时),对象构造完全灵活。

另请参阅:

附带说明, 查看 Kotlin 的对象注册表类型库,它类似于依赖注入,为您提供带有注入选项的单例:

我最近写了an article on that topic。 TL;DR 这是我想出的解决方案:

1) 创建一个SingletonHolder class。你只需要写一次:

open class SingletonHolder<out T, in A>(creator: (A) -> T) {
    private var creator: ((A) -> T)? = creator
    @Volatile private var instance: T? = null

    fun getInstance(arg: A): T {
        val i = instance
        if (i != null) {
            return i
        }

        return synchronized(this) {
            val i2 = instance
            if (i2 != null) {
                i2
            } else {
                val created = creator!!(arg)
                instance = created
                creator = null
                created
            }
        }
    }
}

2) 在你的单例中像这样使用它:

class MySingleton private constructor(arg: ArgumentType) {
    init {
        // Init using argument
    }

    companion object : SingletonHolder<MySingleton, ArgumentType>(::MySingleton)
}

单例初始化将是惰性和线程安全的。