每日一问: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() {
}
})
}