Kotlin & Java 之單利模式

weixin_34293059發表於2018-03-31
    在 Java 中單例模式的寫法存在N種寫法,這裡只列舉其中的幾種。
  • 第一種: 只適合單執行緒環境,懶漢模式
    class UserProFile {

        private static UserProFile Instance = null;

        public UserProFile() {
        }

        public static UserProFile getInstance() {
            if (Instance == null) {
                Instance = new UserProFile();
            }
            return Instance;
        }
    }
  • 第二種:
    class UserProFile {

        private static UserProFile Instance = new UserProFile();

        public UserProFile() {
        }

        public static UserProFile getInstance() {
            return Instance;
        }
    }
  • 第三種:
    class UserProFile {

        private static UserProFile Instance = null;

        public UserProFile() {
        }

        public static UserProFile getInstance() {
            if (Instance == null) {
                synchronized (UserProFile.class) {
                    if (Instance == null) {
                        Instance = new UserProFile();
                    }
                }
            }
            return Instance;
        }
    }
  • 第四種: 關於第四種就是採用 volatile 關鍵字的 DoubleCheck 寫法
    class UserProFile {

        private volatile static UserProFile Instance;

        public static UserProFile getInstance() {
            UserProFile inst = Instance;
            if (inst == null) {
                synchronized (UserProFile.class) {
                    inst = Instance;
                    if (inst == null) {
                        inst = new UserProFile();
                        Instance = inst;
                    }
                }
            }
            return inst;
        }
    }


---

- 這裡討論下 DoubleCheck 單利的寫法

關於 DoubleCheck 這種單利寫法, 在實際開發中是能夠保護執行緒安全的, 比如第三種單例寫法進行了雙重判斷, 線上程A進行訪問的時候,執行緒B也請求過來了,這時就會出現物件錯亂的情況,
那麼在第四種單例模式中新增了 volatile 關鍵字之後, 就不會出現類似問題了, 因為 volatile 關鍵字可以保證物件在例項化以及物件的呼叫是有序的, 比如線上程A中在例項的時候執行緒B看到的例項
賦值以及構造方法其實是有序的呼叫, 先呼叫構造方法例項化完成之後才給 inst 賦值, 那也就是說 如果 inst 為 Null 一定是沒有初始化完成, 如果 inst 不為 Null 那麼一定初始化完成。


---
  • Kotlin 中 DoubleCheck 的寫法
        class UserProFile {

            companion object {

                val instances by lazy(mode =  LazyThreadSafetyMode.SYNCHRONIZED){
                    UserProFile()
                }

                private @Volatile var mInstance : UserProFile? = null

                fun getInstance(): UserProFile {

                    if (null == mInstance){

                        synchronized(this){
                            if (null == mInstance){
                                mInstance = UserProFile()
                            }
                        }
                    }
                    return mInstance!!
                }
            }

        }
  • 在 kotlin 中通過 lazy 關鍵字來進行懶載入, 這裡的 mode 為 SYNCHRONIZED
    。在 Kotlin 中 LazyThreadSafetyMode 一共有三個屬性
       SYNCHRONIZED

       PUBLICATION

       NONE
  • 下面為 Kotlin 的原始碼

      /**
       * Specifies how a [Lazy] instance synchronizes initialization among multiple threads.
       */
      public enum class LazyThreadSafetyMode {
    
          /**
           * Locks are used to ensure that only a single thread can initialize the [Lazy] instance.
           */
          SYNCHRONIZED,
    
          /**
           * Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value,
           * but only the first returned value will be used as the value of [Lazy] instance.
           */
          PUBLICATION,
    
          /**
           * No locks are used to synchronize an access to the [Lazy] instance value; if the instance is accessed from multiple threads, its behavior is undefined.
           *
           * This mode should not be used unless the [Lazy] instance is guaranteed never to be initialized from more than one thread.
           */
          NONE,
      }
    
  • 如果不採用 lazy 關鍵字實現單例模式,在 Java 與 Kotlin 中還有一種寫法,程式碼如下

    class UserProFiles private constructor(){

      companion object {
          fun getInstance() = Holder.mInstance
      }
    
      private object Holder {
          val mInstance = UserProFiles()
      }
    

    }

  • Java 程式碼這裡的寫法幾乎一致, 依然是採用靜態內部類來做處理,這種寫法就不用我們去考慮執行緒安全了,因為 Java 虛擬機器已經幫我們完成這個操作了。

相關文章