1、 饿汉式
在类被初始化时就已经在内存中创建了对象,以空间换时间,故不存在线程安全问题,但是会使类加载变慢。
- Java实现
public class SingletonDemo {
private SingletonDemo () {
}
// 在类加载时就完成了初始化,使得类加载较慢
private static SingletonDemo instance = new SingletonDemo ();
public static SingletonDemo getInstance() {
return instance ;
}
}
- Kotlin实现
object SingletonDemo {
}
- Kotlin编译成的Java字节
public final class SingletonDemo {
public static final SingletonDemo instance ;
static {
SingletonDemo var0 = new SingletonDemo();
instance = var0;
}
}
2、 懒汉式 - 非线程安全
在方法被调用后才创建对象,以时间换空间,在多线程环境下存在风险。
- Java
public class SingletonDemo {
private SingletonDemo () {
}
private static SingletonDemo instance ;
public static SingletonDemo getInstance() {
if (instance == null) {
instance = new SingletonDemo ();
}
return instance ;
}
}
- Kotlin
class SingletonDemo {
private constructor() {
}
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
fun get(): SingletonDemo {
return instance!!
}
}
}
- Kotlin编译成的Java字节
public final class SingletonDemo {
private static SingletonDemo instance;
public static final SingletonDemo.Companion Companion = new SingletonDemo.Companion((DefaultConstructorMarker)null);
private SingletonDemo() {
}
// $FF: synthetic method
public SingletonDemo(DefaultConstructorMarker $constructor_marker) {
this();
}
...
public static final class Companion {
private final SingletonDemo getInstance() {
if (SingletonDemo.instance == null) {
SingletonDemo.instance = new SingletonDemo((DefaultConstructorMarker)null);
}
return SingletonDemo.instance;
}
private final void setInstance(SingletonDemo var1) {
SingletonDemo.instance = var1;
}
@NotNull
public final SingletonDemo get() {
SingletonDemo var10000 = ((SingletonDemo.Companion)this).getInstance();
if (var10000 == null) {
Intrinsics.throwNpe();
}
return var10000;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
3、 懒汉式 - 线程安全
相比上种非线程安全模式,对getInstance()方法添加了同步锁,保证线程安全。但是此种方式在有多个线程同时执行时,每次执行getInstance()方法时都要先获得锁再去执行方法体,如果没有锁,就要等待,耗时长。
- Java
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){}
public static synchronized SingletonDemo getInstance(){
if(instance== null){
instance = new SingletonDemo();
}
return instance;
}
}
- Kotlin
class SingletonDemo {
private constructor() {}
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
@Synchronized
fun get(): SingletonDemo{
return instance!!
}
}
}
- Kotlin编译成的Java字节
public final class SingletonDemo {
private static SingletonDemo instance;
public static final SingletonDemo.Companion Companion = new SingletonDemo.Companion((DefaultConstructorMarker)null);
private SingletonDemo() {
}
// $FF: synthetic method
public SingletonDemo(DefaultConstructorMarker $constructor_marker) {
this();
}
...
public static final class Companion {
private final SingletonDemo getInstance() {
if (SingletonDemo.instance == null) {
SingletonDemo.instance = new SingletonDemo((DefaultConstructorMarker)null);
}
return SingletonDemo.instance;
}
private final void setInstance(SingletonDemo var1) {
SingletonDemo.instance = var1;
}
@NotNull
public final synchronized SingletonDemo get() {
SingletonDemo var10000 = ((SingletonDemo.Companion)this).getInstance();
if (var10000 == null) {
Intrinsics.throwNpe();
}
return var10000;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
4、双重检测锁
只有在对象需要被使用时才创建,第一次判断 instance == null为了避免非必要加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全。(并且一定要加上volatile 关键字,用于防止指令重排序问题发生)
- Java
public class SingletonDemo {
private volatile static SingletonDemo instance;
private SingletonDemo(){}
public static SingletonDemo getInstance(){
if(instance == null){
synchronized (SingletonDemo.class){
if(instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
}
- Kotlin
class SingletonDemo {
private constructor() {}
companion object {
val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonDemo()
}
}
}
- Kotlin编译成的Java字节
public final class SingletonDemo {
@NotNull
private static final Lazy instance$delegate;
public static final SingletonDemo.Companion Companion = new SingletonDemo.Companion((DefaultConstructorMarker)null);
private SingletonDemo() {
}
static {
instance$delegate = LazyKt.lazy(LazyThreadSafetyMode.SYNCHRONIZED, (Function0)null.INSTANCE);
}
// $FF: synthetic method
public SingletonDemo(DefaultConstructorMarker $constructor_marker) {
this();
}
...
public static final class Companion {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(SingletonDemo.Companion.class), "instance", "getInstance()Lcom/homeprint/module/mine/viewmodel/SingletonDemo;"))};
@NotNull
public final SingletonDemo getInstance() {
Lazy var1 = SingletonDemo.instance$delegate;
KProperty var3 = $$delegatedProperties[0];
return (SingletonDemo)var1.getValue();
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
这里面使用到了Kotlin的延迟属性 Lazy,Lazy 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。并且 Lazy 只能用于val修饰的对象。
- 此处在附上Kotlin可带参数的双重检测锁模式
class SingletonDemo {
private constructor(args: Int) {
}
companion object {
@Volatile
private var instance: SingletonDemo? = null
fun getInstance(args: Int) =
instance ?: synchronized(this) {
instance ?: SingletonDemo(args).also { instance = it }
}
}
}
5、静态内部类
外部类加载时并不需要立即加载内部类,内部类不被加载则不会去初始化 instance ,就不会占内存。不仅确保了线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。但是有一个致命缺点,无法传递参数。
- Java
public class SingletonDemo {
private SingletonDemo(){
}
private static class SingletonHolder{
private static SingletonDemo instance = new SingletonDemo();
}
public static SingletonDemo getInstance(){
return SingletonHolder.instance;
}
}
- Kotlin
class SingletonDemo {
private constructor() {}
companion object {
val instance = SingletonHolder.holder
}
private object SingletonHolder {
val holder = SingletonDemo()
}
}
- Kotlin编译成的Java字节
public final class SingletonDemo {
@NotNull
private static final SingletonDemo instance;
public static final SingletonDemo.Companion Companion = new SingletonDemo.Companion((DefaultConstructorMarker)null);
private SingletonDemo() {
}
static {
instance = SingletonDemo.SingletonHolder.INSTANCE.getHolder();
}
// $FF: synthetic method
public SingletonDemo(DefaultConstructorMarker $constructor_marker) {
this();
}
...
private static final class SingletonHolder {
@NotNull
private static final SingletonDemo holder;
public static final SingletonDemo.SingletonHolder INSTANCE;
@NotNull
public final SingletonDemo getHolder() {
return holder;
}
static {
SingletonDemo.SingletonHolder var0 = new SingletonDemo.SingletonHolder();
INSTANCE = var0;
holder = new SingletonDemo((DefaultConstructorMarker)null);
}
}
...
public static final class Companion {
@NotNull
public final SingletonDemo getInstance() {
return SingletonDemo.instance;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
枚举
枚举在 java 中与普通类一样,都能拥有字段与方法,而且枚举实例创建是线程安全的,在任何情况下,它都是一个单例,只是在Android中枚举类太过于耗费资源。此处做一下补充。
- Java
public enum SingletonDemo {
INSTANCE(0xFF0000)
private int rgb;
SingletonDemo(int rgb){
this.rgb = rg
}
}
// 调用方法
SingletonDemo .INSTANCE
- Kotlin
enum class SingletonDemo (val rgb: Int) {
INSTANCE(0xFF0000)
}
// 调用方法
SingletonDemo .INSTANCE
推荐文章
深入理解单例模式
Kotlin下的5种单例模式
原文博客:Kotlin学习笔记:Kotlin中五种单例模式的实现