美文网首页
kotlin协程:使用协程,如何获取单例对象

kotlin协程:使用协程,如何获取单例对象

作者: Xigong | 来源:发表于2021-03-10 11:55 被阅读0次

    目前找到三种方式

    1. 切换到单线程获取单例
    2. 使用Coroutine提供的Mutex获取单例
    3. 使用CAS(AtomicReference)获取单例
    
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.sync.Mutex
    import kotlinx.coroutines.sync.withLock
    import kotlinx.coroutines.withContext
    import java.util.concurrent.atomic.AtomicReference
    
    
    
    /** 使用协程,如何获取单例对象 */
    class SingleInstance private constructor(val token: String) {
        companion object {
            @Volatile
            private var instance: SingleInstance? = null
    
            /** 切换到同一个线程实现 */
            suspend fun getInstance(): SingleInstance {
                 return withContext(Dispatchers.Main.immediate) {
                    instance ?: createInstance().also {
                        instance = it
                    }
                }
            }
    
            /** Mutex不是可重入的,要注意死锁的问题 */
            private val mutex = Mutex()
    
            /** 使用Coroutine提供的Mutex实现 */
            suspend fun getInstance2(): SingleInstance {
                return mutex.withLock {
                    instance ?: createInstance().also {
                        instance = it
                    }
                }
            }
    
            private var instanceRef = AtomicReference<SingleInstance>()
    
            /** 使用CAS实现*/
            suspend fun getInstance3(): SingleInstance {
                while (true) {
                    val instance = instanceRef.get()
                    if (instance != null) {
                        return instance
                    }
                    // createInstance可能会调用多次,但是只会设置成功一次
                    instanceRef.compareAndSet(null, createInstance())
                }
            }
    
            private suspend fun createInstance(): SingleInstance {
                val token = getTokenFromRemote()
                return SingleInstance(token)
            }
        }
    }
    
    /** 模拟网络请求获取token */
    private suspend fun getTokenFromRemote(): String {
        return withContext(Dispatchers.IO) {
            delay(500)
            "This is the token request by retrofit"
        }
    }
    

    三种方式的对比:

    1. 切换到单线程的方式
    • 优点:逻辑简单
    • 缺点:切换线程的开销和延迟,不适用没有固定单线程的系统(比如非GUI的Java项目中没有MainThread)
    • 使用场景: 不追求极致性能的场景
    1. 使用Mutex的方式
    • 优点:没有切换线程的开销和延迟
    • 缺点:锁不能可重入的,如果情况比较复杂很容易发生死锁
    • 使用场景: 使用锁比较少的场景
    1. CAS的方式
    • 优点:无锁,没有切换线程的开销和延迟
    • 缺点:对象实例可能会创建多个
    • 使用场景: 适合创建对象资源消耗比较小,只需要使用单例来保证系统的状态唯一性的情况

    另外注意:

    • 不能使用sychronized 和Java的ReentrantLock来实现,因为java的锁都是锁线程,而协程在同一个函数里,可能会发生线程切换,所以无法使用java的线程锁

    相关文章

      网友评论

          本文标题:kotlin协程:使用协程,如何获取单例对象

          本文链接:https://www.haomeiwen.com/subject/iohwqltx.html