每日一问:Java与Kotlin的单例实现


Java中单例的实现

1.懒汉,线程不安全

//懒汉式单例类.在第一次调用的时候实例化自己 
public class Singleton {
    
    
    private Singleton() {
    
    }
    private static Singleton single=null;
    //静态工厂方法 
    public static Singleton getInstance() {
    
    
         if (single == null) {
    
      
             single = new Singleton();
         }  
        return single;
    }
}

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问(这里不考虑反射)。这种写法lazy loading很明显,但是是在多线程不能安全。

2.懒汉getInstance加同步锁

为了确保线程安全,可以在getInstance方法上加同步锁

public static synchronized Singleton getInstance() {
    
    
         if (single == null) {
    
      
             single = new Singleton();
         }  
        return single;
}

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

3.双重校验锁

public class Singleton {
    
      
     private volatile static Singleton singleton;  
     private Singleton (){
    
    }   
     public static Singleton getSingleton() {
    
      
     if (singleton == null) {
    
      
         synchronized (Singleton.class) {
    
      
         if (singleton == null) {
    
      
             singleton = new Singleton();  
         }  
         }  
     }  
     return singleton;  
     }  
 }

4.静态内部类

public class Singleton {
    
      
    private static class LazyHolder {
    
      
       private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){
    
    }  
    public static final Singleton getInstance() {
    
      
       return LazyHolder.INSTANCE;  
    }  
}
  • 这种方式也利用了classloder的机制来保证初始化instance时只有一个线程,既实现了线程安全,又避免了同步带来的性能影响;
  • 它跟第三种和第四种方式不同的是:第2种和第3种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。
  • 因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。

5.饿汉

public class Singleton {
    
      
     private static Singleton instance = new Singleton();  
     private Singleton (){
    
    }
     public static Singleton getInstance() {
    
      
     return instance;  
     }  
 }

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。但初始化instance没有达到lazy loading的效果。

6.饿汉,变种

public class Singleton {
    
      
      private Singleton instance = null;  
      static {
    
      
      instance = new Singleton();  
      }  
      private Singleton (){
    
    }
      public static Singleton getInstance() {
    
      
      return this.instance;  
      }  
 }

表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance

想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。

7.枚举

public enum Singleton {
    
      
     INSTANCE;  
     public void doSomeMethd() {
    
      
     }  
 }

这种方式不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

kotlin中单例的实现

1. 懒汉式

//Kotlin实现
class SingletonDemo private constructor() {
    
    
    companion object {
    
    
        private var instance: SingletonDemo? = null
            get() {
    
    
                if (field == null) {
    
    
                    field = SingletonDemo()
                }
                return field
            }
        fun get(): SingletonDemo{
    
    
        //细心的小伙伴肯定发现了,这里不用getInstance作为为方法名,是因为在伴生对象声明时,内部已有getInstance方法,所以只能取其他名字
         return instance!!
        }
    }
}

上述代码中,我们可以发现在Kotlin实现中,我们让其主构造函数私有化并自定义了其属性访问器,其余内容大同小异。

2.懒汉getInstance加同步锁

//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!!
        }
    }

}

在java版本中介绍了,懒汉式会出现线程安全的问题,需要使用使用同步锁,在Kotlin中,如果你需要将方法声明为同步,需要添加@Synchronized注解。

3.双重校验锁:(带属性参数)

//kotlin实现
package com.sunsta.bear.magic
class SingletonDemo private constructor() {
    
    
    companion object {
    
    
        val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
    
    
            SingletonDemo()
        }
    }
}

其中我们用到了Kotlin的延迟属性 Lazy,Lazy是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

4.静态内部类

//kotlin实现
class SingletonDemo private constructor() {
    
    
    companion object {
    
    
        val instance = SingletonHolder.holder
    }

    private object SingletonHolder {
    
    
        val holder= SingletonDemo()
    }
}

静态内部类的实现方式Kotlin与Java实现基本相同

5.饿汉

//Kotlin实现
object SingletonDemo

object 关键字就完成java中相同的功能

撇开kotlin中协程的写法不看,看一个我rxjava实战的案例方法:这种写法非常的优雅和舒服。

 companion object {
    
    
        val instance: GlideEngine by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
    
    
            GlideEngine()
        }
    }

fun blur(frameView: View, coverView: View, imgUrl: String, factor: Int, corner: Int, roundPx: Int) {
    
    
        Observable.just(imgUrl)
            .map {
    
     imageHttp: String? ->
                if (corner == 0 || roundPx == 0) {
    
    
                    BitmapUtils.getBitmap(imageHttp)
                } else {
    
    
                    BitmapUtils.fillet(BitmapUtils.getBitmap(imageHttp), roundPx, corner)
                }
            }
            .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<Bitmap> {
    
    
                override fun onSubscribe(d: Disposable) {
    
    }
                override fun onNext(it: Bitmap) {
    
    
                    frameView.background = BitmapDrawable(frameView.context.resources, it)
                    blurDouble(frameView, coverView, factor, corner, roundPx)
                }
                override fun onError(e: Throwable) {
    
    }
                override fun onComplete() {
    
    }
            })
    }

猜你喜欢

转载自blog.csdn.net/u014657752/article/details/125344460