Android中的单例模式(包含Java、Kotlin)

  在Android开发工程中,单例模式可以说是我们使用得非常频繁的设计模式了。常见的写法有5种:

  1. 饿汉式
  2. 懒汉式
  3. 同步锁
  4. 双重校验
  5. 内部类

下面我们对这5种写法的Java、Kotlin各自举例。调用统一由Kotlin调用(其实差别并不大)

一、饿汉式

java:

public class BaseSingleton {
    private static final String TAG = BaseSingleton.class.getSimpleName();
    private BaseSingleton(){
        Log.e(TAG,"BaseSingleton init");
    }
    private static BaseSingleton INSTANCE = new BaseSingleton();
    public static BaseSingleton getInstance(){
        return INSTANCE;
    }
    public void sayHello(String name){
        Log.e(TAG,"Hello "+name+",nice to meet you !");
    }
}

Kotlin:

object KBaseSingleton {
    @JvmStatic
    fun sayHello(name:String){
        Log.e(this::class.java.simpleName,"Hell $name,nice to meet you !")
    }
}

是不是觉得Kotlin特别的简单?是的,Kotlin一个Object就可以标记一个单例了。

调用:

BaseSingleton.getInstance().sayHello("Calm")
KBaseSingleton.sayHello("Calm")

可以看到这种写法很简单,但静态变量的代码在类加载则会执行,不管后续是否会使用,单例就会存在了。如果程序一定会用到这个单例,则此写法适用。

二、懒汉式

Java:

public class LazySingleton {
    private static final String TAG = BaseSingleton.class.getSimpleName();
    private LazySingleton(){
        Log.e(TAG,"LazySingleton init");
    }
    private static LazySingleton INSTANCE;
    public static LazySingleton getInstance(){
        if(INSTANCE == null){
            INSTANCE = new LazySingleton();
        }
        return INSTANCE;
    }
    public void sayHello(String name){
        Log.e(TAG,"Hello "+name+",nice to meet you !");
    }
}

Kotlin:

class KLazySingleton private constructor(){
    companion object {
        //原生写法
        val INSTANCE by lazy(LazyThreadSafetyMode.NONE) {
            KLazySingleton()
        }
        //翻译Java
        private var INSTANCE1:KLazySingleton? = null
        fun getInstance():KLazySingleton{
            if(INSTANCE1 == null){
                INSTANCE1 = KLazySingleton()
            }
            return INSTANCE1!!
        }
    }
    fun sayHello(name:String){
        Log.e(this::class.java.simpleName,"Hell $name,nice to meet you !")
    }
}

可以看到此种写法静态变量开始并不赋值,在调用时进行空判断并进行赋值。那么如果我们同时多个线程来调用,是否如我们所希望只有一个实例呢?我们写如下调用代码:

for (i in 0..20){
  Thread(
     Runnable {
        LazySingleton.getInstance().sayHello("Calm$i")                      
        Log.e(this::class.java.simpleName,Thread.currentThread().id.toString())
     }
  ).start()
}

循环开启20个线程来调用单例,其输出结果如下:

10-12 14:07:33.920 8361-8470/com.calm.singleton E/LazySingleton: LazySingleton init
10-12 14:07:33.922 8361-8470/com.calm.singleton E/LazySingleton: Hello Calm0,nice to meet you !
10-12 14:07:33.924 8361-8471/com.calm.singleton E/LazySingleton: LazySingleton init
10-12 14:07:33.925 8361-8471/com.calm.singleton E/LazySingleton: Hello Calm1,nice to meet you !
10-12 14:07:33.926 8361-8470/com.calm.singleton E/MainActivity: 4614
10-12 14:07:33.926 8361-8471/com.calm.singleton E/MainActivity: 4615

这里我只截取了出现问题的部分,可以看到由于多线程调用而导致单例其实是被初始化了2次的,存在2个实例,而不是我们希望的单例。可见此种写法如果要用于多线程,是不合适的。

三、同步锁

Java:

public class LazySyncSingleton {
    private static final String TAG = LazySyncSingleton.class.getSimpleName();
    private LazySyncSingleton(){
        Log.e(TAG,"LazySyncSingleton init");
    }
    private static LazySyncSingleton INSTANCE;
    public static synchronized LazySyncSingleton getInstance(){
        if(INSTANCE == null){
            INSTANCE = new LazySyncSingleton();
        }
        return INSTANCE;
    }
    public void sayHello(String name){
        Log.e(TAG,"Hello "+name+",nice to meet you !");
    }
}

Kotlin:

class KLazySyncSingleton {
    companion object {
        private var INSTANCE:KLazySyncSingleton? = null
        @Synchronized
        fun getInstance():KLazySyncSingleton{
            if(INSTANCE == null){
                INSTANCE = KLazySyncSingleton()
            }
            return INSTANCE!!
        }
    }
    fun sayHello(name:String){
        Log.e(this::class.java.simpleName,"Hell $name,nice to meet you !")
    }
}

可以看到,其实此种方式与懒汉式区别只是加上了@Synchronized 标识,完美解决多线程而导致的非单例问题。只是在每次获取单例的时候都加了锁,效率上有所牺牲。于是,第四种写法应运而生。

四、双重校验

Java:

public class DoubleCheckSingleton {
    private static final String TAG = DoubleCheckSingleton.class.getSimpleName();
    private DoubleCheckSingleton(){
        Log.e(TAG,"DoubleCheckSingleton init");
    }
    private static volatile DoubleCheckSingleton INSTANCE;
    public static DoubleCheckSingleton getInstance(){
        if(INSTANCE == null){
            synchronized (DoubleCheckSingleton.class){
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckSingleton();
                }
            }
        }
        return INSTANCE;
    }
    public void sayHello(String name){
        Log.e(TAG,"Hello "+name+",nice to meet you !");
    }
}

Kotlin:

class KDoubleCheckSingleton {
    companion object {
        //原生写法
        val INSTANCE by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            KDoubleCheckSingleton()
        }
        //翻译写法
        @Volatile
        private var INSTANCE1:KDoubleCheckSingleton? = null
        fun getInstance():KDoubleCheckSingleton{
            if(INSTANCE1 == null){
                synchronized(DoubleCheckSingleton::class){
                    if(INSTANCE1 == null){
                        INSTANCE1 = KDoubleCheckSingleton()
                    }
                }
            }
            return INSTANCE1!!
        }
    }
    fun sayHello(name:String){
        Log.e(this::class.java.simpleName,"Hell $name,nice to meet you !")
    }
}

此种写法较同步锁写法,只在第一次获取单例的时候加锁,之后则不需要在加锁,提升了第一次之后获取单例的效率。

五、内部类

Java:

public class InnerSingleton {
    private static final String TAG = InnerSingleton.class.getSimpleName();
    private InnerSingleton(){
        Log.e(TAG,"InnerSingleton init");
    }
    private static class Holer{
        private static InnerSingleton INSTANCE = new InnerSingleton();
    }
    public static InnerSingleton getInstance(){
        return Holer.INSTANCE;
    }
    public void sayHello(String name){
        Log.e(TAG,"Hello "+name+",nice to meet you !");
    }
}

Kotlin:

class KInnerSingleton {
    private object Holder{
        val INSTANCE = KInnerSingleton()
    }
    companion object {
        fun getInstance() = Holder.INSTANCE
    }
    fun sayHello(name:String){
        Log.e(this::class.java.simpleName,"Hell $name,nice to meet you !")
    }
}

惊到了没?依靠静态内部类保证了线程同步,又满足了在getInstance()时才创建单例的懒汉式。比较推荐的一种写法。

单例的写法具体使用哪一种,还是要依赖于我们实际的使用场景来进行选择,而不是一味的哪一种好。其实他们都有各自的优缺点,依场景选择即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值