synchronized關(guān)鍵字的內(nèi)存語義及實現(xiàn)

1.同步的語義

下面的內(nèi)容摘自JSR 133 FAQ:

Synchronization has several aspects. The most well-understood is mutual exclusion -- only one thread can hold a monitor at once, so synchronizing on a monitor means that once one thread enters a synchronized block protected by a monitor, no other thread can enter a block protected by that monitor until the first thread exits the synchronized block.

同步有幾個方面骚腥。最容易理解的是互斥 —— 只有一個線程可以立即持有一個監(jiān)視器,因此在監(jiān)視器上進行同步意味著一旦一個線程進入由一個監(jiān)視器保護的同步塊贫导,則其他線程都不能進入該監(jiān)視器保護的塊练对,直到第一個線程退出同步塊拴清。

But there is more to synchronization than mutual exclusion. Synchronization ensures that memory writes by a thread before or during a synchronized block are made visible in a predictable manner to other threads which synchronize on the same monitor. After we exit a synchronized block, we release the monitor, which has the effect of flushing the cache to main memory, so that writes made by this thread can be visible to other threads. Before we can enter a synchronized block, we acquire the monitor, which has the effect of invalidating the local processor cache so that variables will be reloaded from main memory. We will then be able to see all of the writes made visible by the previous release.

但是同步不僅僅是互斥。 同步確保以可預(yù)見的方式,使線程在同步塊之前或期間對內(nèi)存的寫入對于在同一監(jiān)視器上同步的其他線程可見。 退出同步塊后壳嚎,我們 釋放 該監(jiān)視器,其有將緩存刷新到主內(nèi)存的效果末早, 以便該線程進行的寫入對于其他線程可見烟馅。 在我們進入一個同步塊之前,我們需要 獲取 該監(jiān)視器然磷,該監(jiān)視器具有使本地處理器緩存無效的作用焙糟,以便可以從主內(nèi)存中重新加載變量。 然后样屠,我們將能夠看到以前釋放中所有可見的寫入。

Discussing this in terms of caches, it may sound as if these issues only affect multiprocessor machines. However, the reordering effects can be easily seen on a single processor. It is not possible, for example, for the compiler to move your code before an acquire or after a release. When we say that acquires and releases act on caches, we are using shorthand for a number of possible effects.

從高速緩存的角度進行討論缺脉,聽起來似乎這些問題僅影響多處理器計算機痪欲。 但是,重排序效果可以在單個處理器上輕松看到攻礼。 例如业踢,編譯器不可能在獲取之前或釋放之后移動代碼。 當(dāng)我們說獲取和釋放作用于緩存時礁扮,我們使用簡寫來表示多種可能的影響知举。

The new memory model semantics create a partial ordering on memory operations (read field, write field, lock, unlock) and other thread operations (start and join), where some actions are said to happen before other operations. When one action happens before another, the first is guaranteed to be ordered before and visible to the second. The rules of this ordering are as follows:

新的內(nèi)存模型語義在內(nèi)存操作(讀字段,寫字段太伊,鎖定雇锡,解鎖)和其他線程操作( start 和 join )上創(chuàng)建了部分排序,其中某些操作據(jù)說 happen before其他操作僚焦。 當(dāng)一個動作在另一個動作之前發(fā)生時锰提,第一個動作被確保排序在第二個動作之前并且對于第二個動作可見。 此排序規(guī)則如下:

  • Each action in a thread happens before every action in that thread that comes later in the program's order.
    線程中的每個動作先于該線程中的在程序順序上后出現(xiàn)的每個動作發(fā)生芳悲。

  • An unlock on a monitor happens before every subsequent lock on that same monitor.
    監(jiān)視器上的一個解鎖發(fā)生在 同一個 監(jiān)視器上的每個后續(xù)鎖定之前立肘。

  • A write to a volatile field happens before every subsequent read of that same volatile.
    對 volatile 字段的每個寫操作發(fā)生在每次后續(xù)讀取 同一個 volatile之前。

  • A call to start() on a thread happens before any actions in the started thread.
    一個對線程的 start() 的調(diào)用發(fā)生在被啟動線程中的任何操作之前名扛。

  • All actions in a thread happen before any other thread successfully returns from a join() on that thread.
    線程中的所有操作發(fā)生在其他線程成功從該線程上的 join() 返回之前谅年。

This means that any memory operations which were visible to a thread before exiting a synchronized block are visible to any thread after it enters a synchronized block protected by the same monitor, since all the memory operations happen before the release, and the release happens before the acquire.

這意味著線程在退出同步塊之前對一個線程可見的任何內(nèi)存操作,在進入受同一監(jiān)視器保護的同步塊之后對于任何線程都是可見的肮韧,因為所有內(nèi)存操作都發(fā)生在釋放之前融蹂,而釋放發(fā)生在獲取之前旺订。

可以看到同步的語義包含兩點:一個是互斥,一個是保證可見性殿较。

2 synchronized的基本使用

根據(jù)Java 語言規(guī)范可知:

Java里面的每個對象都關(guān)聯(lián)著一個 monitor耸峭,一個線程可以 lock 或者 unlock這個 monitor。

  • 對于一個類方法淋纲,該方法所在類的Class對象關(guān)聯(lián)的monitor被使用劳闹。

  • 對于一個實例方法,與this(某個調(diào)用該方法的實例對象)關(guān)聯(lián)的monitor被使用洽瞬。

  • 對于一個同步塊本涕,即synchronized(obj){....},與obj關(guān)聯(lián)的monitor被使用。

舉個栗子:

package synchronizedTest;
 
class Test {
    int count;
    //實例同步方法
    synchronized void bump() {
        count++;
    }
    static int classCount;
    //類同步方法
    static synchronized void classBump() {
        classCount++;
    }
}

上面的代碼等價于:

package synchronizedTest;
 
class BumpTest {
    int count;
    void bump() {
        //同步塊
        synchronized (this) { count++; }
    }
    static int classCount;
    static void classBump() {
        try {
             //同步塊
            synchronized (Class.forName("BumpTest")) {
                classCount++;
            }
        } catch (ClassNotFoundException e) {}
    }
}

3.從JVM字節(jié)碼層面看同步塊

反解析下上面兩個類對應(yīng)的字節(jié)碼文件伙窃。

編譯成class文件

javac synchronizedTest/Test.java

將Class文件反匯編下

javap -p -v synchronizedTest/Test > synchronizedTest/Test.disasm

類似地:

javac synchronizedTest/BumpTest.java
javap -p -v synchronizedTest/BumpTest > synchronizedTest/BumpTest.disasm

下面重點看下Test.disasm和BumpTest.disasm

BumpTest.bump方法節(jié)選

void bump();
  descriptor: ()V
  flags:
  Code:
    stack=3, locals=3, args_size=1
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: aload_0
       5: dup
       6: getfield      #2                  // Field count:I
       9: iconst_1
      10: iadd
      11: putfield      #2                  // Field count:I
      14: aload_1
      15: monitorexit
      16: goto          24
      19: astore_2
      20: aload_1
      21: monitorexit
      22: aload_2
      23: athrow
      24: return
    Exception table:
       from    to  target type
           4    16    19   any
          19    22    19   any
    LineNumberTable:
      line 6: 0
      line 7: 24

3.1 monitor_enter的說明:lock特定對象的monitor菩颖。

The objectref must be of type reference.
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

objectref必須是引用類型。
每個對象都與一個監(jiān)視器關(guān)聯(lián)为障。 監(jiān)視器只有在擁有所有者的情況下才被鎖定晦闰。 執(zhí)行monitorenter的線程嘗試獲得與objectref關(guān)聯(lián)的監(jiān)視器的所有權(quán),如下所示:
如果與objectref關(guān)聯(lián)的監(jiān)視器的條目計數(shù)為零鳍怨,則線程進入監(jiān)視器并將其條目計數(shù)設(shè)置為1呻右。 然后,該線程是監(jiān)視器的所有者鞋喇。
如果線程已經(jīng)擁有與objectref關(guān)聯(lián)的監(jiān)視器声滥,則它將重新進入監(jiān)視器,從而條目計數(shù)加1侦香。
如果另一個線程已經(jīng)擁有與objectref關(guān)聯(lián)的監(jiān)視器落塑,則該線程將阻塞,直到該監(jiān)視器的條目計數(shù)為零為止罐韩,然后再次嘗試獲取所有權(quán)憾赁。

3.2 monitor_exit的說明: unlock特定對象的monitor。

The objectref must be of type reference.
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

objectref必須是引用類型散吵。
執(zhí)行monitorexit的線程必須是與objectref引用的實例相關(guān)聯(lián)的監(jiān)視器的所有者缠沈。
該線程減少與objectref關(guān)聯(lián)的監(jiān)視器的條目計數(shù)。 結(jié)果错蝴,如果條目計數(shù)的值為零洲愤,則線程退出監(jiān)視器,并且不再是其所有者顷锰。 其他被阻塞進入監(jiān)視器的線程可以嘗試進入監(jiān)視器柬赐。

3.3 下面是關(guān)于異常表( Exception table)的說明:

上面是bump方法對應(yīng)的指令,異常表有兩行(如下所示)官紫,每一行稱為異常表條目:

 4    16    19   any   
 19    22    19   any

每個異常表條目監(jiān)控[from, to)的字節(jié)碼肛宋,如果出現(xiàn)異常州藕,則跳轉(zhuǎn)到target指針對應(yīng)的字節(jié)碼執(zhí)行,type則代表該處理器所能捕獲的異常類型(any代表任何異常)酝陈。
對應(yīng)的上面兩個異常條目的意思就是:

  • 4對應(yīng)from床玻,16對應(yīng)to, 19對應(yīng)target, any對應(yīng)type沉帮;也就是[4,16)指向的字節(jié)碼指令拋任何異常(any)了锈死,都會跳轉(zhuǎn)到19執(zhí)行。
  • 19對應(yīng)from穆壕,22對應(yīng)to, 19對應(yīng)target待牵, any對應(yīng)type;也就是[19, 22)指向的字節(jié)碼拋出任何異常(any)了喇勋,都會跳轉(zhuǎn)到19執(zhí)行缨该。

也就是

  • 情況一:[4,16)執(zhí)行沒有任何異常,則goto到24川背,返回贰拿。在這種情況下正常加鎖 3: monitorenter,釋放鎖 15: monitorexit
  • 情況二:[4,16)拋出任何異常熄云,都跳轉(zhuǎn)19壮不,都會執(zhí)行到 21: monitorexit;如果成功了皱碘,則異常結(jié)束;如果在[19, 22)執(zhí)行中拋出任何異常隐孽,就跳轉(zhuǎn)到19再重新執(zhí)行一遍癌椿。

通過上面的分析,我們可以發(fā)現(xiàn)菱阵,不管是否拋出異常踢俄,synchronized 同步塊,都會釋放之前獲取的鎖晴及,也就是 monitorenter 與 monitorexit 始終是成對出現(xiàn)的都办。

BumpTest.classBump和BumpTest.bump是類似,你可以自己嘗試分析下虑稼。

4.從JVM字節(jié)碼層面看同步方法

Test.bump節(jié)選

synchronized void bump();
  descriptor: ()V
  flags: ACC_SYNCHRONIZED
  Code:
    stack=3, locals=1, args_size=1
       0: aload_0
       1: dup
       2: getfield      #2                  // Field count:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field count:I
      10: return
    LineNumberTable:
      line 6: 0
      line 7: 10

Java虛擬機規(guī)范可知

Monitor entry on invocation of a synchronized method, and monitor exit on its return, are handled implicitly by the Java Virtual Machine's method invocation and return instructions, as if monitorenter and monitorexit were used.

Java虛擬機的方法調(diào)用和返回指令琳钉,隱式處理了調(diào)用同步方法時的monitor entry 和返回時的monitor exit,就像使用了monitorenter 和monitorexit 一樣蛛倦。

所以歌懒,同步方法和同步塊的實現(xiàn)方式本質(zhì)上并沒有什么不同。

5.從機器碼看synchronized

假如我們把BumpTest改成如下代碼:

package synchronizedTest;
class BumpTest {
    int count;
    void bump() {
        synchronized (this) { count++; }
    }
    static int classCount;

    static void classBump() {
        try {
            synchronized (Class.forName("BumpTest")) {
                classCount++;
            }
        } catch (ClassNotFoundException e) {}
    }
    public static void main(String[] args){
        for(int i=0; i< 100000; i++){
            classBump();
        }
        System.out.println(classCount);
    }
}

然后重新編譯成字節(jié)碼文件溯壶,再得到本地機器指令文件(注意及皂,執(zhí)行第二條指令還需要hsdis,你可以參考我之前的文章安裝)

javac synchronizedTest/BumpTest.java
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly synchronizedTest/BumpTest > synchronizedTest/BumpTest.native

然后我們搜索'classBump'甫男,發(fā)現(xiàn)了下面的機器指令,可以看到在Intel64 CPU 下验烧,synchronized 最終還是用 lock cmpxchg 實現(xiàn)的板驳。

在這里插入圖片描述

從這里我們可以發(fā)現(xiàn),這里的 synchronized 和之前講的volatile碍拆, Unsafe中的CAS若治,剛好是用類似的原子指令(比如這里的lock cmpxchg)實現(xiàn)的

至于BumpTest 和Test中其他同步方法或同步塊倔监,你可以試一下直砂,結(jié)果是一致的。

6. 同步

但是浩习,并不是所有的同步都是用上述的原子指令實現(xiàn)的(其實是輕量級鎖)静暂,而是根據(jù)不同情況使用不同的鎖,鎖的類型分為重量級鎖谱秽,輕量級鎖洽蛀,偏向鎖。 下面主要簡單地說明這三種鎖的實現(xiàn)疟赊。

HotSpot JVM 使用一個 two-word 對象頭郊供,第一個word是 Class pointer,第二個是 Mark word 用來保存同步近哟,GC 和 hashCode 等相關(guān)信息驮审。Mark word使用方式見下圖:


Mark word

6.1 重量級鎖 heavyweight monitor

重量級鎖對應(yīng)上述 Mark word 的 tag bits 為10的情況,即此時狀態(tài)為inflated吉执。

重量級鎖會使用操作系統(tǒng)級別的鎖定原語 ( OS-level locking primitives疯淫, 比如 pthread mutex) 來實現(xiàn)。 這些操作將涉及系統(tǒng)調(diào)用戳玫,需要從操作系統(tǒng)的用戶態(tài)切換至內(nèi)核態(tài)熙掺,其開銷非常之大。重量級鎖可以在所有場景使用咕宿。

6.2 輕量級鎖 lightweight lock

輕量級鎖對應(yīng)上述 Mark word 的 tag bits 為 00 的情況币绩,即此時狀態(tài)為 lightweight-locked。

輕量級鎖是對重量級鎖的優(yōu)化府阀。輕量級鎖使用一個或兩個 CPU 級別的原子指令(比如 lock cmpxchg)充蓝,從而避免了使用操作系統(tǒng)級別的鎖定原語蔫骂。
可選的輕量級鎖實現(xiàn)算法有 Metalock (CAS in both acquire and release), Thin Locks(CAS in acquire), 和 Relaxed-locks (CAS in acquire).

但輕量級鎖適用范圍有限芭碍,以 Thin Locks 為例子垮兑,它適用于這樣的對象,這些對象不被爭用川队,不需要對自己執(zhí)行 wait力细,notify 或 notifyAll 操作睬澡,并且沒有鎖定到過多的嵌套深度。絕大多數(shù)對象都滿足上述條件眠蚂; 那些不滿足條件的對象的鎖要用重量級鎖來實現(xiàn)煞聪。

6.3 偏向鎖 biased lock

偏向鎖在 JDK6 引入,對應(yīng)上述 Mark word 的 tag bits 為 01 的情況逝慧,即此時狀態(tài)為 unlocked 或 biasable昔脯。

偏向鎖是對輕量級鎖的再優(yōu)化,嘗試在 acquire 和 release 中避免原子指令, 僅在第一次獲取時執(zhí)行一次原子指令笛臣,以將鎖定線程 ID 安裝到 mark word 中云稚。

但偏向鎖的適用范圍相對輕量級鎖來說更加有限,偏向鎖適用于單個進程反復(fù)獲取并釋放鎖沈堡,而其他進程很少訪問該鎖的情況静陈,即大多數(shù)對象在其生命周期中最多只能被一個線程鎖定的情況。

更多關(guān)于偏向鎖的知識诞丽,請見 Quickly Reacquirable Locks Biased Locking in HotSpot

6.4 鎖的轉(zhuǎn)換

在 HotSpot JVM 中鲸拥,按照偏向鎖,輕量級鎖僧免,重量級鎖的順序來嘗試獲取對象的鎖刑赶。完整流程見下圖:

在這里插入圖片描述

偏向鎖相關(guān)流程(對應(yīng)上圖中以1開頭的):
如果新分配的對象O是可偏向的但未被偏向(對應(yīng)上圖中1),那么第一次鎖定的時候使用 CAS 在 mark word 中插入線程T1的ID(對應(yīng)上圖中1-1)懂衩,而接下來的鎖定僅僅將 mark word 里面的 線程T1的ID 與 當(dāng)前線程T2的比較撞叨,此時可能出現(xiàn)兩種情況:

  • 情況1:如果線程ID一樣,則表明對象O 已偏向當(dāng)前線程 T2浊洞,也就是當(dāng)前線程 T2 已經(jīng)鎖定對象 O牵敷,可以無需 CAS 即可 lock/unlock (對應(yīng)圖中1-2
  • 情況2:如果線程ID不一樣,則撤銷對T1的偏向, 并需要檢查對象O是否可以重偏向沛申。如果可以重偏向,則將對象O重偏向到線程T2(對應(yīng)上圖中1-3)姐军;否則將撤銷偏向并回退到正常鎖定流程(對應(yīng)上圖中以2開頭的)铁材,此后對象 O 對應(yīng)的類不可以再被偏向鎖定。

輕量級鎖和重量級鎖流程(對應(yīng)上圖中以2開頭的):
如果新分配的對象O對應(yīng)的類不可偏向奕锌,則先嘗試通過 CAS 設(shè)置 mark word來獲取輕量級鎖著觉。如果成功,則獲取輕量級鎖惊暴;如果失敗饼丘,則先判斷是否是遞歸鎖定,如果是則表明已經(jīng)獲取鎖辽话,如果不是則膨脹為重量級鎖肄鸽。

輕量級鎖定時卫病,每次進入同步方法,都會在棧幀中生成一個新的 lock record (鎖記錄)典徘,該鎖記錄有兩個字段displaced hdr和owner蟀苛,displaced hdr 用來保存鎖對象的對象頭mark word, owner用來保存指向鎖對象的指針逮诲。另外帜平,Lock record出于內(nèi)存對齊的要求,會確保lock record的存儲地址最后兩位為00 梅鹦,這兩位剛好用來作為輕量級鎖的標(biāo)識裆甩。

輕量級鎖定:嘗試將 lock record 的 displaced hdr 用來保存鎖對象原來的mark word;將lock record的owner指向鎖對象齐唆;將鎖對象原來的mark word 替換為指向lock record的指針嗤栓;這三步都會在同一個CAS原子地進行嘗試。
如果CAS 成功蝶念,則表明獲取輕量級鎖成功抛腕,也就是下圖所示的情況


在這里插入圖片描述

如果CAS 失敗,則分為遞歸鎖定和需要膨脹到重量級鎖兩種情況處理媒殉。
虛擬機首先測試對象的 mark word 是否指向當(dāng)前線程的方法棧担敌。

  • 如果是,則表明是遞歸鎖定廷蓉,當(dāng)前線程已經(jīng)擁有對象的鎖全封,可以安全地繼續(xù)執(zhí)行它。對于這種遞歸鎖定的對象桃犬,將 lock record 初始化為0而不是對象的 mark word刹悴。(對應(yīng)上圖中的2-2
  • 如果不是,則表明存在兩個不同的線程同時在同一個對象上同步攒暇,這時需要將輕量級鎖膨脹到重量級鎖土匀,也就是將指向heavy monitor的指針賦值給 對象的 mark word(對應(yīng)上圖中的2-3

上面只是關(guān)于同步流程的部分總結(jié),關(guān)于同步更全面的介紹形用,請參見Sun 的 Eliminating Synchronization Related Atomic Operations with Biased Locking and Bulk RebiasingSynchronization in Java SE 6(HotSpot) 就轧,以及 Synchronization我翻譯的Synchronization中英對照版 ,還有 Java虛擬機是怎么實現(xiàn)synchronized的田度? 妒御,以及在 bytecodeInterpreter.cpp 搜索Lock method if synchronized

7.總結(jié)

這篇文章首先對 synchronized 的基本使用進行了復(fù)習(xí)镇饺;然后嘗試從字節(jié)碼和本地機器碼的角度上看 synchronized 的實現(xiàn)乎莉;最后通過查看官方文檔弄清synchronized 的實現(xiàn)會分別嘗試偏向鎖(嘗試避免原子指令,僅第一次的時候需要使用原子指令,以將鎖定線程的 ID 安裝到 header word 中)惋啃,輕量級鎖(在鎖定和解鎖中使用 一個或兩個CPU-level 的原子指令)哼鬓,重量級鎖(操作系統(tǒng)級調(diào)用),這三種鎖實現(xiàn)適用范圍越來越大肥橙,但代價也越來越大魄宏。

其實 synchronized 如何實現(xiàn)對于一般人是無感的,這也是為什么每次 JDK 發(fā)布都可能會改善它的性能存筏,我們要做的基本上是根據(jù) JDK 版本理解對應(yīng)的實現(xiàn)宠互,然后調(diào)整一下 相應(yīng)的 JVM 參數(shù)。

8.參考

1.Java語言規(guī)范第八版第17章
2.Java虛擬機規(guī)范第八版
3.https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
4.https://time.geekbang.org/column/article/13530
5.https://blogs.oracle.com/dave/biased-locking-in-hotspot
6.https://wiki.openjdk.java.net/display/HotSpot/Synchronization
7.https://stackoverflow.com/questions/46312817/does-java-ever-rebias-an-individual-lock
8.https://www.zhihu.com/question/57774251

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末椭坚,一起剝皮案震驚了整個濱河市予跌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌善茎,老刑警劉巖券册,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異垂涯,居然都是意外死亡烁焙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門耕赘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骄蝇,“玉大人,你說我怎么就攤上這事操骡【呕穑” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵册招,是天一觀的道長岔激。 經(jīng)常有香客問我,道長是掰,這世上最難降的妖魔是什么虑鼎? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮键痛,結(jié)果婚禮上炫彩,老公的妹妹穿的比我還像新娘。我一直安慰自己散休,他們只是感情好媒楼,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布乐尊。 她就那樣靜靜地躺著戚丸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上限府,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天夺颤,我揣著相機與錄音,去河邊找鬼胁勺。 笑死世澜,一個胖子當(dāng)著我的面吹牛寥裂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播案疲,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼褐啡!你這毒婦竟也來了诺舔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褥赊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體司光,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡售躁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年啡直,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舷丹。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瑞信,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出精肃,到底是詐尸還是另有隱情筐眷,我是刑警寧澤武翎,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站综芥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚜枢,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昭殉。 院中可真熱鬧挪丢,春花似錦任内、人聲如沸死嗦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舍哄。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工扁达, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蠢熄。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓跪解,卻偏偏與公主長得像,于是被迫代替她去往敵國和親签孔。 傳聞我的和親對象是個殘疾皇子叉讥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354