當Kotlin完美邂逅設(shè)計模式之單例模式(一)

test100.png

簡述: 從這篇文章開始掸驱,我將帶領(lǐng)大家一起來探討一下Kotlin眼中的設(shè)計模式肛搬。說下為什么想著要開始這么一個系列文章。主要基于下面幾點原因:

  • 1毕贼、設(shè)計模式一直是開發(fā)者看懂Android源碼的一個很大障礙温赔。所以想要理解和運用源碼中一些設(shè)計思想和技巧,首先看懂源碼是第一步鬼癣,而看懂源碼让腹,又得需要設(shè)計模式和數(shù)據(jù)結(jié)構(gòu)算法(我的每周一算法和數(shù)據(jù)結(jié)構(gòu)文章系列也開始了)作為基礎(chǔ)远剩,否則看起來云里霧里,只能死記硬背別人總結(jié)的結(jié)論骇窍,最終還是無法消化和理解運用瓜晤。
  • 2、Kotlin中設(shè)計模式的實現(xiàn)和Java的實現(xiàn)還是有很大的差別的腹纳,利用Kotlin語言自身的特性實現(xiàn)設(shè)計模式比硬生生套用Java中的設(shè)計模式實現(xiàn)要更優(yōu)雅和更高效痢掠。當然每個設(shè)計模式我會對比Java與Kotlin實現(xiàn)區(qū)別,以便理解更加深刻淹辞。
  • 3象缀、據(jù)了解Kotlin有關(guān)設(shè)計模式實現(xiàn)的文章目前在國內(nèi)還是比較少的央星,所以想系統(tǒng)地去寫一個有關(guān)Kotlin邂逅設(shè)計模式的系列文章莉给。

說下最終的目標吧,最終目標是有基礎(chǔ)能力在分析的源碼時候能夠站在一個全局角度去思考州泊,而不是一頭扎入茫茫源碼中無法自拔迷失自我遥皂。后面也會隨即出一些有關(guān)源碼分析的文章。所以請暫時先好好掌握這些基礎(chǔ)的工具样悟。

一窟她、介紹

單例模式是開發(fā)者最為常見的一種設(shè)計模式录肯,也是23種設(shè)計模式中最為簡單一種設(shè)計模式论咏。大部分的開發(fā)者都知道它的使用和原理。單例模式顧名思義就是在應(yīng)用這個模式時养涮,單例對象的類必須是只有一個對象實例存在贯吓。在一些應(yīng)用場景中我們只需要一個全局唯一的對象實例去調(diào)度整體行為蘸劈。還有一些情況為了系統(tǒng)資源開銷考慮威沫,避免重復(fù)創(chuàng)建多個實例孵构,往往采用單例模式來保證全局只有一個實例對象颈墅。

二、定義

保證某個類只有一個實例對象芹橡,該實例對象在內(nèi)部進行實例化煎殷,并且提供了一個獲取該實例對象的全局訪問點豪直。

三饵撑、基本要求

  • 1滑潘、構(gòu)造器私有化,private修飾粹舵,主要為了防止外部私自創(chuàng)建該單例類的對象實例
  • 2、提供一個該實例對象全局訪問點诅需,在Java中一般是以公有的靜態(tài)方法或者枚舉返回單例類對象
  • 3、在多線程環(huán)境下保證單例類有且只有一個對象實例场刑,以及在多線程環(huán)境下獲取單例類對象實例需要保證線程安全。
  • 4施籍、在反序列化時保證單例類有且只有一個對象實例

四竿裂、使用場景

一般用于確定某個類只需要一個實例對象进副,從而避免中了頻繁創(chuàng)建多個對象實例所帶來資源和性能開銷影斑。例如常見的數(shù)據(jù)庫連接或IO操作等矫户。

五、UML類圖

image

六、餓漢式單例

餓漢式單例模式是實現(xiàn)單例模式比較簡單的一種方式空另,它有個特點就是不管需不需要該單例實例都弹,該實例對象都會被實例化畅厢。

1、Kotlin實現(xiàn)

在Kotlin中實現(xiàn)一個餓漢式單例模式可以說是非常非常簡單,只需要定義一個object對象表達式即可,無需手動去設(shè)置構(gòu)造器私有化和提供全局訪問點专筷,這一點Kotlin編譯器全給你做好了弱贼。

object KSingleton : Serializable {//實現(xiàn)Serializable序列化接口,通過私有磷蛹、被實例化的readResolve方法控制反序列化
    fun doSomething() {
        println("do some thing")
    }

    private fun readResolve(): Any {//防止單例對象在反序列化時重新生成對象
        return KSingleton//由于反序列化時會調(diào)用readResolve這個鉤子方法吮旅,只需要把當前的KSingleton對象返回而不是去創(chuàng)建一個新的對象
    }
}

//在Kotlin中使用KSingleton
fun main(args: Array<String>) {
    KSingleton.doSomething()//像調(diào)用靜態(tài)方法一樣,調(diào)用單例類中的方法
}
//在Java中使用KSingleton
public class TestMain {
    public static void main(String[] args) {
        KSingleton.INSTANCE.doSomething();//通過拿到KSingleton的公有單例類靜態(tài)實例INSTANCE, 再通過INSTANCE調(diào)用單例類中的方法
    }
}

KSingleton反編譯成Java代碼

public final class KSingleton implements Serializable {
   public static final KSingleton INSTANCE;

   public final void doSomething() {
      String var1 = "do some thing";
      System.out.println(var1);
   }

   private final Object readResolve() {
      return INSTANCE;//可以看到readResolve方法直接返回了INSTANCE而不是創(chuàng)建新的實例
   }

   static {//靜態(tài)代碼塊初始化KSingleton實例味咳,不管有沒有使用庇勃,只要KSingleton被加載了,
   //靜態(tài)代碼塊就會被調(diào)用,KSingleton實例就會被創(chuàng)建堡纬,并賦值給INSTANCE
      KSingleton var0 = new KSingleton();
      INSTANCE = var0;
   }
}

可能會有人疑問: 沒有看到構(gòu)造器私有化碗旅,實際上這一點已經(jīng)在編譯器層面做了限制稼锅,不管你是在Java還是Kotlin中都無法私自去創(chuàng)建新的單例對象痊臭。

2、Java實現(xiàn)

public class Singleton implements Serializable {
    private Singleton() {//構(gòu)造器私有化
    }

    private static final Singleton mInstance = new Singleton();

    public static Singleton getInstance() {//提供公有獲取單例對象的函數(shù)
        return mInstance;
    }

    //防止單例對象在反序列化時重新生成對象
    private Object readResolve() throws ObjectStreamException {
        return mInstance;
    }
}

對比一下Kotlin和Java的餓漢式的單例實現(xiàn)發(fā)現(xiàn),是不是覺得Kotlin會比Java簡單得多得多骤视。

七、線程安全的懶漢式單例

可是有時候我們并不想當類加載的時候就去創(chuàng)建這個單例實例蒋纬,而是想當我們使用這個實例的時候才去初始化它停撞。于是乎就有了懶漢式的單例模式

1、Kotlin實現(xiàn)

class KLazilySingleton private constructor() : Serializable {
    fun doSomething() {
        println("do some thing")
    }
    companion object {
        private var mInstance: KLazilySingleton? = null
            get() {
                return field ?: KLazilySingleton()
            }

        @JvmStatic
        @Synchronized//添加synchronized同步鎖
        fun getInstance(): KLazilySingleton {
            return requireNotNull(mInstance)
        }
    }
    //防止單例對象在反序列化時重新生成對象
    private fun readResolve(): Any {
        return KLazilySingleton.getInstance()
    }
}
//在Kotlin中調(diào)用
fun main(args: Array<String>) {
    KLazilySingleton.getInstance().doSomething()
}
//在Java中調(diào)用
 KLazilySingleton.getInstance().doSomething();

2迹蛤、Java實現(xiàn)

class LazilySingleton implements Serializable {
    private static LazilySingleton mInstance;

    private LazilySingleton() {}//構(gòu)造器私有化

    public static synchronized LazilySingleton getInstance() {//synchronized同步鎖保證多線程調(diào)用getInstance方法線程安全
        if (mInstance == null){
            mInstance = new LazilySingleton();
        }
        return mInstance;
    }
    
    private Object readResolve() throws ObjectStreamException {//防止反序列化
        return mInstance;
    }
}

八身坐、DCL(double check lock)改造懶漢式單例

我們知道線程安全的單例模式直接是使用synchronized同步鎖抹腿,鎖住getInstance方法,每一次調(diào)用該方法的時候都得獲取鎖贸桶,但是如果這個單例已經(jīng)被初始化了,其實按道理就不需要申請同步鎖了,直接返回這個單例類實例即可拄踪。于是就有了DCL實現(xiàn)單例方式救恨。

1伤靠、Java中DCL實現(xiàn)

//DCL實現(xiàn)單例模式
public class LazySingleTon implements Serializable {
    //靜態(tài)成員私有化捣域,注意使用volatile關(guān)鍵字,因為會存在DCL失效的問題
    private volatile static LazySingleTon mInstance = null; 

    private LazySingleTon() { //構(gòu)造器私有化
    }

    //公有獲取單例對象的函數(shù)
    //DCL(Double Check Lock) 既能在需要的時候初始化單例宴合,又能保證線程安全焕梅,且單例對象初始化完后,調(diào)用getInstance不需要進行同步鎖
    public static LazySingleTon getInstance() {
        if (mInstance == null) {//為了防止單例對象初始化完后卦洽,調(diào)用getInstance再次重復(fù)進行同步鎖
            synchronized (LazySingleTon.class) {
                if (mInstance == null) {
                    mInstance = new LazySingleTon();
                }
            }
        }

        return mInstance;
    }

    private Object readResolve() throws ObjectStreamException {
        return mInstance;
    }
}

2贞言、Kotlin中DCL實現(xiàn)

在Kotlin中有個天然特性可以支持線程安全DCL的單例,可以說也是非常非常簡單阀蒂,就僅僅3行代碼左右该窗,那就是Companion Object + lazy屬性代理,一起來看下吧蚤霞。

class KLazilyDCLSingleton private constructor() : Serializable {//private constructor()構(gòu)造器私有化

    fun doSomething() {
        println("do some thing")
    }

    private fun readResolve(): Any {//防止單例對象在反序列化時重新生成對象
        return instance
    }
    
    companion object {
        //通過@JvmStatic注解酗失,使得在Java中調(diào)用instance直接是像調(diào)用靜態(tài)函數(shù)一樣,
        //類似KLazilyDCLSingleton.getInstance(),如果不加注解昧绣,在Java中必須這樣調(diào)用: KLazilyDCLSingleton.Companion.getInstance().
        @JvmStatic
        //使用lazy屬性代理规肴,并指定LazyThreadSafetyMode為SYNCHRONIZED模式保證線程安全
        val instance: KLazilyDCLSingleton by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { KLazilyDCLSingleton() }
    }
}

//在Kotlin中調(diào)用,直接通過KLazilyDCLSingleton類名調(diào)用instance
fun main(args: Array<String>) {
    KLazilyDCLSingleton.instance.doSomething()
}
//在Java中調(diào)用
public class TestMain {
    public static void main(String[] args) {
    //加了@JvmStatic注解后夜畴,可以直接KLazilyDCLSingleton.getInstance()拖刃,不會打破Java中調(diào)用習(xí)慣,和Java調(diào)用方式一樣贪绘。
       KLazilyDCLSingleton.getInstance().doSomething();
       //沒有加@JvmStatic注解序调,只能這樣通過Companion調(diào)用
       KLazilyDCLSingleton.Companion.getInstance().doSomething();
    }
}

注意: 建議上面例子中添加@JvmStatic注解,Kotlin這門語言可謂是操碎了心兔簇,做的很小心翼翼发绢,為了不讓Java開發(fā)者打破他們的調(diào)用習(xí)慣,讓調(diào)用根本無法感知到是Kotlin編寫垄琐,因為外部調(diào)用方式和Java方式一樣边酒。如果硬生生把Companion對象暴露給Java開發(fā)者他們可能會感到一臉懵逼。

可能大家對lazy和Companion Object功能強大感到一臉懵狸窘,讓我們一起瞅瞅反編譯后的Java代碼你就會恍然大悟了:

public final class KLazilyDCLSingleton implements Serializable {
   @NotNull
   private static final Lazy instance$delegate;
   //Companion提供公有全局訪問點墩朦,KLazilyDCLSingleton.Companion實際上一個餓漢式的單例模式
   public static final KLazilyDCLSingleton.Companion Companion = new KLazilyDCLSingleton.Companion((DefaultConstructorMarker)null);
   public final void doSomething() {
      String var1 = "do some thing";
      System.out.println(var1);
   }

   private final Object readResolve() {
      return Companion.getInstance();
   }

   private KLazilyDCLSingleton() {
   }

   static {//注意: 可以看到靜態(tài)代碼塊中并不是初始化KLazilyDCLSingleton的instance而是初始化它的Lazy代理對象,說明KLazilyDCLSingleton類被加載了翻擒,
   //但是KLazilyDCLSingleton的instance并沒有被初始化氓涣,符合懶加載規(guī)則牛哺,那么什么時候初始化instance這就涉及到了屬性代理知識了,下面會做詳細分析
      instance$delegate = LazyKt.lazy(LazyThreadSafetyMode.SYNCHRONIZED, (Function0)null.INSTANCE);
   }

   // $FF: synthetic method
   public KLazilyDCLSingleton(DefaultConstructorMarker $constructor_marker) {
      this();
   }

   @NotNull
   public static final KLazilyDCLSingleton getInstance() {
      return Companion.getInstance();//這里可以看到加了@JvmStatic注解后劳吠,getInstance內(nèi)部把我們省略Companion.getInstance()這一步引润,這樣一來Java調(diào)用者就直接KLazilyDCLSingleton.getInstance()獲取單例實例
   }

   //Companion靜態(tài)內(nèi)部類實際上也是一個單例模式
   public static final class Companion {
      // $FF: synthetic field
      static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(KLazilyDCLSingleton.Companion.class), "instance", "getInstance()Lcom/mikyou/design_pattern/singleton/kts/KLazilyDCLSingleton;"))};

      /** @deprecated */
      // $FF: synthetic method
      @JvmStatic
      public static void instance$annotations() {
      }

      @NotNull
      //這個方法需要注意,最終instance初始化和獲取將在這里進行
      public final KLazilyDCLSingleton getInstance() {
         //拿到代理對象
         Lazy var1 = KLazilyDCLSingleton.instance$delegate;
         KProperty var3 = $$delegatedProperties[0];
         //代理對象的getValue方法就是初始化instance和獲取instance的入口痒玩。內(nèi)部會判斷instance是否被初始化過沒有就會返回新創(chuàng)建的對象淳附,
         //初始化過直接返回上一次初始化的對象。所以只有真正調(diào)用getInstance方法需要這個實例的時候instance才會被初始化蠢古。
         return (KLazilyDCLSingleton)var1.getValue();
      }

      private Companion() {//Companion構(gòu)造器私有化
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }

3奴曙、Kotlin的lazy屬性代理內(nèi)部實現(xiàn)源碼分析

//expect關(guān)鍵字標記這個函數(shù)是平臺相關(guān),我們需要找到對應(yīng)的actual關(guān)鍵字實現(xiàn)表示平臺中一個相關(guān)實現(xiàn) 
public expect fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T>

//對應(yīng)多平臺中一個平臺相關(guān)實現(xiàn)lazy函數(shù)
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {//根據(jù)不同mode草讶,返回不同的Lazy的實現(xiàn)洽糟,我們重點看下SynchronizedLazyImpl
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE//為了解決DCL帶來指令重排序?qū)е轮鞔婧凸ぷ鲀?nèi)存數(shù)據(jù)不一致的問題,這里使用Volatile原語注解堕战。具體Volatile為什么能解決這樣的問題請接著看后面的分析
    private val lock = lock ?: this

    override val value: T
        get() {//當外部調(diào)用value值脊框,get訪問器會被調(diào)用
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {//進行第一層的Check, 如果這個值已經(jīng)初始化過了,直接返回_v1践啄,避免走下面synchronized獲取同步鎖帶來不必要資源開銷浇雹。
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {//進行第二層的Check,主要是為了_v2被初始化直接返回
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                //如果沒有初始化執(zhí)行initializer!!() lambda, 
                //實際上相當于執(zhí)行外部調(diào)用傳入的 by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { KLazilyDCLSingleton() } 中的KLazilyDCLSingleton()也即是返回KLazilyDCLSingleton實例對象
                    val typedValue  initializer!!()
                    _value = typedValue//并把這個實例對象保存在_value中
                    initializer = null
                    typedValue
                }
            }
        }

    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE

    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(): Any = InitializedLazyImpl(value)
}    

4屿讽、DCL存在多線程安全問題分析及解決

  • 問題分析:

DCL存在多線程安全問題昭灵,我們都知道線程安全主要來自主存和工作內(nèi)存數(shù)據(jù)不一致以及重排序(指令重排序或編譯器重排序造成的)。那么DCL存在什么問題呢伐谈?
首先烂完,mInstance = new LazySingleton() 不是一個原子操作而是分為三步進行:

  • 1、給LazySingleton實例分配內(nèi)存
  • 2诵棵、調(diào)用LazySingleton的構(gòu)造函數(shù)抠蚣,初始化成員字段
  • 3、將mInstance對象引用指向分配的內(nèi)存空間(此時mInstance不為null)

在JDK1.5之前版本的Java內(nèi)存模型中履澳,Cache,寄存器到主存回寫順序規(guī)則嘶窄,無法保證第2和第3執(zhí)行的順序,可能是1-2-3距贷,也有可能是1-3-2
若A線程先執(zhí)行了第1步柄冲,第3步,此時切換到B線程忠蝗,由于A線程中已經(jīng)執(zhí)行了第3步所以mInstance不為null现横,那么B線程中直接把mInstance取走,由于并沒有執(zhí)行第2步使用的時候就會報錯。

  • 解決問題:

為了解決該問題戒祠,JDK1.5之后骇两,具體化了volatile關(guān)鍵字,能夠確保每次都是從主存獲取最新有效值姜盈。所以需要private volatile static LazySingleTon mInstance = null;

九低千、靜態(tài)內(nèi)部類單例

DCL雖然在一定程度上能解決資源消耗、多余synchronized同步贩据、線程安全等問題栋操,但是某些情況下還會存在DCL失效問題闸餐,盡管在JDK1.5之后通過具體化volatile原語來解決DCL失效問題饱亮,但是它始終并不是優(yōu)雅一種解決方式,在多線程環(huán)境下一般不推薦DCL的單例模式舍沙。所以引出靜態(tài)內(nèi)部類單例實現(xiàn)

1近上、Kotlin實現(xiàn)

class KOptimizeSingleton private constructor(): Serializable {//private constructor()構(gòu)造器私有化
    companion object {
        @JvmStatic
        fun getInstance(): KOptimizeSingleton {//全局訪問點
            return SingletonHolder.mInstance
        }
    }

    fun doSomething() {
        println("do some thing")
    }
    
    private object SingletonHolder {//靜態(tài)內(nèi)部類
        val mInstance: KOptimizeSingleton = KOptimizeSingleton()
    }
    
    private fun readResolve(): Any {//防止單例對象在反序列化時重新生成對象
        return SingletonHolder.mInstance
    }
}

2、Java實現(xiàn)

//使用靜態(tài)內(nèi)部單例模式
public class OptimizeSingleton implements Serializable {
    //構(gòu)造器私有化
    private OptimizeSingleton() {
    }

    //靜態(tài)私有內(nèi)部類
    private static class SingletonHolder {
        private static final OptimizeSingleton sInstance = new OptimizeSingleton();
    }

    //公有獲取單例對象的函數(shù)
    public static OptimizeSingleton getInstance() {
        return SingletonHolder.sInstance;
    }
    
    public void doSomeThings() {
        System.out.println("do some things");
    }
    
    //防止反序列化重新創(chuàng)建對象
    private Object readResolve() {
        return SingletonHolder.sInstance;
    }
}

十拂铡、枚舉單例

其實細心的小伙伴就會觀察到上面例子中我都會去實現(xiàn)Serializable接口壹无,并且會去實現(xiàn)readResolve方法。這是為了反序列化會重新創(chuàng)建對象而使得原來的單例對象不再唯一感帅。通過序列化一個單例對象將它寫入到磁盤中斗锭,然后再從磁盤中讀取出來,從而可以獲得一個新的實例對象失球,即使構(gòu)造器是私有的岖是,反序列化會通過其他特殊途徑創(chuàng)建單例類的新實例。然而為了讓開發(fā)者能夠控制反序列化实苞,提供一個特殊的鉤子方法那就是readResolve方法豺撑,這樣一來我們只需要在readResolve直接返回原來的實例即可,就不會創(chuàng)建新的對象黔牵。

枚舉單例實現(xiàn)聪轿,就是為了防止反序列化,因為我們都知道枚舉類反序列化是不會創(chuàng)建新的對象實例的猾浦。 Java的序列化機制對枚舉類型做了特殊處理陆错,一般來說在序列枚舉類型時,只會存儲枚舉類的引用和枚舉常量名稱金赦,反序列化的過程中危号,這些信息被用來在運行時環(huán)境中查找存在的枚舉類型對象,枚舉類型的序列化機制保證只會查找已經(jīng)存在的枚舉類型實例素邪,而不是創(chuàng)建新的實例外莲。

1、Kotlin實現(xiàn)

enum class KEnumSingleton {
    INSTANCE;

    fun doSomeThing() {
        println("do some thing")
    }
}
//在Kotlin中調(diào)用
fun main(args: Array<String>) {
    KEnumSingleton.INSTANCE.doSomeThing()
}
//在Java中調(diào)用
 KEnumSingleton.INSTANCE.doSomeThing();

2、Java實現(xiàn)

public enum EnumSingleton {
    INSTANCE;
    public void doSomeThing() {
        System.out.println("do some thing");
    }
}

//調(diào)用方式
EnumSingleton.INSTANCE.doSomeThing();

<div align="center"><img src="https://user-gold-cdn.xitu.io/2018/5/14/1635c3fb0ba21ec1?w=430&h=430&f=jpeg&s=39536" width="200" height="200"></div>

歡迎關(guān)注Kotlin開發(fā)者聯(lián)盟偷线,這里有最新Kotlin技術(shù)文章磨确,每周會不定期翻譯一篇Kotlin國外技術(shù)文章。如果你也喜歡Kotlin声邦,歡迎加入我們~~~

Kotlin系列文章乏奥,歡迎查看:

數(shù)據(jù)結(jié)構(gòu)與算法系列:

Kotlin 原創(chuàng)系列:

Effective Kotlin翻譯系列

翻譯系列:

實戰(zhàn)系列:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骗炉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蛇受,更是在濱河造成了極大的恐慌句葵,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兢仰,死亡現(xiàn)場離奇詭異乍丈,居然都是意外死亡,警方通過查閱死者的電腦和手機把将,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門轻专,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人察蹲,你說我怎么就攤上這事请垛。” “怎么了递览?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵叼屠,是天一觀的道長。 經(jīng)常有香客問我绞铃,道長镜雨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任儿捧,我火速辦了婚禮荚坞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菲盾。我一直安慰自己颓影,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布懒鉴。 她就那樣靜靜地躺著诡挂,像睡著了一般碎浇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上璃俗,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天奴璃,我揣著相機與錄音,去河邊找鬼城豁。 笑死苟穆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的唱星。 我是一名探鬼主播雳旅,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼间聊!你這毒婦竟也來了攒盈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤甸饱,失蹤者是張志新(化名)和其女友劉穎沦童,沒想到半個月后仑濒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叹话,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年墩瞳,在試婚紗的時候發(fā)現(xiàn)自己被綠了驼壶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡喉酌,死狀恐怖热凹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泪电,我是刑警寧澤般妙,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站相速,受9級特大地震影響碟渺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜突诬,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一苫拍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旺隙,春花似錦绒极、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春铡俐,著一層夾襖步出監(jiān)牢的瞬間摘昌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工高蜂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留聪黎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓备恤,卻偏偏與公主長得像稿饰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子露泊,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容