java自旋鎖 去適應(yīng)不同的CPU處理技術(shù)

主要的CPU處理技術(shù):多線程、多核心弟跑、SMP(Symmetric Multi-Processing對稱多處理結(jié)構(gòu))、NUMA技術(shù)席吴、亂序執(zhí)行夭织、分枝技術(shù)吭露、控制器 來自百度百科中央處理器CPU。目前主流的查百度都是說SMP/MPP/NUMA這三種尊惰。

1 SMP (Symmetric Multi Processing),對稱多處理系統(tǒng)內(nèi)有許多緊耦合多處理器讲竿,在這樣的系統(tǒng)中,所有的CPU共享全部資源弄屡,如總線题禀,內(nèi)存和I/O系統(tǒng)等,操作系統(tǒng)或管理數(shù)據(jù)庫的復(fù)本只有一個(gè)膀捷,這種系統(tǒng)有一個(gè)最大的特點(diǎn)就是共享所有資源迈嘹。多個(gè)CPU之間沒有區(qū)別,平等地訪問內(nèi)存全庸、外設(shè)秀仲、一個(gè)操作系統(tǒng)。操作系統(tǒng)管理著一個(gè)隊(duì)列壶笼,每個(gè)處理器依次處理隊(duì)列中的進(jìn)程神僵。如果兩個(gè)處理器同時(shí)請求訪問一個(gè)資源(例如同一段內(nèi)存地址),由硬件拌消、軟件的鎖機(jī)制去解決資源爭用問題挑豌。Access to RAM is serialized; this and cache coherency issues causes performance to lag slightly behind the number of additional processors in the system.

SMP.png

SMP 服務(wù)器 CPU 利用率最好的情況是 2 至 4 個(gè) CPU

2 NUMA(Non-Uniform Memory Access) 由于 SMP 在擴(kuò)展能力上的限制,人們開始探究如何進(jìn)行有效地?cái)U(kuò)展從而構(gòu)建大型系統(tǒng)的技術(shù)墩崩, NUMA 就是這種努力下的結(jié)果之一。利用 NUMA 技術(shù)侯勉,可以把幾十個(gè) CPU( 甚至上百個(gè) CPU) 組合在一個(gè)服務(wù)器內(nèi)鹦筹。

NUMA CPU 模塊結(jié)構(gòu)

3 MPP(Massive Parallel Processing) 和 NUMA 不同, MPP 提供了另外一種進(jìn)行系統(tǒng)擴(kuò)展的方式址貌,它由多個(gè) SMP 服務(wù)器通過一定的節(jié)點(diǎn)互聯(lián)網(wǎng)絡(luò)進(jìn)行連接铐拐,協(xié)同工作,完成相同的任務(wù)练对,從用戶的角度來看是一個(gè)服務(wù)器系統(tǒng)遍蟋。其基本特征是由多個(gè) SMP 服務(wù)器 ( 每個(gè) SMP 服務(wù)器稱節(jié)點(diǎn) ) 通過節(jié)點(diǎn)互聯(lián)網(wǎng)絡(luò)連接而成,每個(gè)節(jié)點(diǎn)只訪問自己的本地資源 ( 內(nèi)存螟凭、存儲等 ) 虚青,是一種完全無共享 (Share Nothing) 結(jié)構(gòu),因而擴(kuò)展能力最好螺男,理論上其擴(kuò)展無限制棒厘,目前的技術(shù)可實(shí)現(xiàn) 512 個(gè)節(jié)點(diǎn)互聯(lián)纵穿,數(shù)千個(gè) CPU 。目前業(yè)界對節(jié)點(diǎn)互聯(lián)網(wǎng)絡(luò)暫無標(biāo)準(zhǔn)奢人,如 NCR 的 Bynet 谓媒, IBM 的 SPSwitch ,它們都采用了不同的內(nèi)部實(shí)現(xiàn)機(jī)制何乎。但節(jié)點(diǎn)互聯(lián)網(wǎng)僅供 MPP 服務(wù)器內(nèi)部使用句惯,對用戶而言是透明的。

MPP CPU 模塊結(jié)構(gòu)

更多三者的閱讀請到SMP支救、NUMA抢野、MPP體系結(jié)構(gòu)介紹

MESI(Modified Exclusive Shared Or Invalid)(也稱為伊利諾斯協(xié)議,是因?yàn)樵搮f(xié)議由伊利諾斯州立大學(xué)提出)是一種廣泛使用的支持寫回策略的緩存一致性協(xié)議搂妻,該協(xié)議被應(yīng)用在Intel奔騰系列的CPU中蒙保,詳見“support the more efficient write-back cache in addition to the write-through cache previously used by the Intel 486 processor
MESI協(xié)議中的狀態(tài)
CPU中每個(gè)緩存行(caceh line)使用4種狀態(tài)進(jìn)行標(biāo)記(使用額外的兩位(bit)表示):
M: 被修改(Modified)
該緩存行只被緩存在該CPU的緩存中,并且是被修改過的(dirty),即與主存中的數(shù)據(jù)不一致欲主,該緩存行中的內(nèi)存需要在未來的某個(gè)時(shí)間點(diǎn)(允許其它CPU讀取請主存中相應(yīng)內(nèi)存之前)寫回(write back)主存邓厕。當(dāng)被寫回主存之后,該緩存行的狀態(tài)會變成獨(dú)享(exclusive)狀態(tài)扁瓢。
E: 獨(dú)享的(Exclusive)
該緩存行只被緩存在該CPU的緩存中详恼,它是未被修改過的(clean),與主存中數(shù)據(jù)一致引几。該狀態(tài)可以在任何時(shí)刻當(dāng)有其它CPU讀取該內(nèi)存時(shí)變成共享狀態(tài)(shared)昧互。同樣地,當(dāng)CPU修改該緩存行中內(nèi)容時(shí)伟桅,該狀態(tài)可以變成Modified狀態(tài)敞掘。
S: 共享的(Shared)
該狀態(tài)意味著該緩存行可能被多個(gè)CPU緩存,并且各個(gè)緩存中的數(shù)據(jù)與主存數(shù)據(jù)一致(clean)楣铁,當(dāng)有一個(gè)CPU修改該緩存行中玖雁,其它CPU中該緩存行可以被作廢(變成無效狀態(tài)(Invalid))。
I: 無效的(Invalid)
該緩存是無效的(可能有其它CPU修改了該緩存行)盖腕。
操作:
在一個(gè)典型系統(tǒng)中赫冬,可能會有幾個(gè)緩存(在多核系統(tǒng)中,每個(gè)核心都會有自己的緩存)共享主存總線溃列,每個(gè)相應(yīng)的CPU會發(fā)出讀寫請求劲厌,而緩存的目的是為了減少CPU讀寫共享主存的次數(shù)。
一個(gè)緩存除在Invalid狀態(tài)外都可以滿足cpu的讀請求听隐,一個(gè)invalid的緩存行必須從主存中讀炔贡恰(變成S或者 E狀態(tài))來滿足該CPU的讀請求。
一個(gè)寫請求只有在該緩存行是M或者E狀態(tài)時(shí)才能被執(zhí)行,如果緩存行處于S狀態(tài)辽幌,必須先將其它緩存中該緩存行變成Invalid狀態(tài)(也既是不允許不同CPU同時(shí)修改同一緩存行增淹,即使修改該緩存行中不同位置的數(shù)據(jù)也不允許)。該操作經(jīng)常作用廣播的方式來完成乌企,例如:Request For Ownership (RFO)
緩存可以隨時(shí)將一個(gè)非M狀態(tài)的緩存行作廢虑润,或者變成Invalid狀態(tài),而一個(gè)M狀態(tài)的緩存行必須先被寫回主存加酵。
一個(gè)處于M狀態(tài)的緩存行必須時(shí)刻監(jiān)聽所有試圖讀該緩存行相對就主存的操作拳喻,這種操作必須在緩存將該緩存行寫回主存并將狀態(tài)變成S狀態(tài)之前被延遲執(zhí)行。
一個(gè)處于S狀態(tài)的緩存行也必須監(jiān)聽其它緩存使該緩存行無效或者獨(dú)享該緩存行的請求猪腕,并將該緩存行變成無效(Invalid)冗澈。
一個(gè)處于E狀態(tài)的緩存行也必須監(jiān)聽其它緩存讀主存中該緩存行的操作,一旦有這種操作陋葡,該緩存行需要變成S狀態(tài)亚亲。
對于M和E狀態(tài)而言總是精確的,他們在和該緩存行的真正狀態(tài)是一致的腐缤。而S狀態(tài)可能是非一致的捌归,如果一個(gè)緩存將處于S狀態(tài)的緩存行作廢了,而另一個(gè)緩存實(shí)際上可能已經(jīng)獨(dú)享了該緩存行岭粤,但是該緩存卻不會將該緩存行升遷為E狀態(tài)惜索,這是因?yàn)槠渌彺娌粫V播他們作廢掉該緩存行的通知,同樣由于緩存并沒有保存該緩存行的copy的數(shù)量剃浇,因此(即使有這種通知)也沒有辦法確定自己是否已經(jīng)獨(dú)享了該緩存行巾兆。
從上面的意義看來E狀態(tài)是一種投機(jī)性的優(yōu)化:如果一個(gè)CPU想修改一個(gè)處于S狀態(tài)的緩存行,總線事務(wù)需要將所有該緩存行的copy變成invalid狀態(tài)虎囚,而修改E狀態(tài)的緩存不需要使用總線事務(wù)角塑。

轉(zhuǎn)換圖.png

好了了解了這些概念后可以來測試了,首先實(shí)現(xiàn)第一個(gè)簡單的自旋鎖 借助AtomicBoolean

package com.alibaba.otter.canal.common;
import java.util.concurrent.atomic.AtomicBoolean;
public class TASLock { //test and set lock 嘗試去set 鎖
    AtomicBoolean state = new AtomicBoolean(false);  
    void lock() {
        while (true) {
             if (state.compareAndSet(state.get(),true)) { 
                 return;
             }  
        }
    } 
    void unLock() {  
        state.set(false);  
    }  
}

然后進(jìn)行優(yōu)化先取得是否被占用再去執(zhí)行占用

package com.alibaba.otter.canal.common;
import java.util.concurrent.atomic.AtomicBoolean;
public class TTASLock { // test for first then test set lock
    AtomicBoolean state = new AtomicBoolean(false);
    void lock() {
        while (true) {
            while (state.get()) {
            }
            if (state.compareAndSet(state.get(), true)) {
                return;
            }
        }
    }
    void unLock() {
        state.set(false);
    }
}

測試類

package com.alibaba.otter.canal.common;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@SuppressWarnings("restriction")
public class TASTest extends AbstractZkTest {
     //并發(fā)線程數(shù)  
    private final static int NTHREAD = 10;  
    //計(jì)數(shù)  
    private static volatile int count = 0;  
    //最大計(jì)數(shù)  
    private final static int MAX = 1000000;  
    //TAS鎖  
    private static TASLock lock = new TASLock();  
    //TTAS鎖  
//    private static TTASLock lock = new TTASLock();  
    @Before
    public void setUp() {
    }
    @After
    public void tearDown() {
    }
    @Test
    public void testUnsafe() throws InterruptedException {
            final CountDownLatch endGate = new CountDownLatch(1);  
            final CountDownLatch startGate = new CountDownLatch(1);  
            ExecutorService e = Executors.newFixedThreadPool(NTHREAD);  
            for (int i = 0; i < NTHREAD; i++) {  
                e.execute(new Runnable() {  
                    @Override  
                    public void run() {  
                        try {  
                            startGate.await();  
                            while (true) {  
                                lock.lock();  
                                if (count < MAX) {  
                                    count++;  
                                    lock.unLock();  
                                    continue;  
                                }  
                                lock.unLock();  
                                break;  
                            }  
                            endGate.countDown();  
                        } catch (InterruptedException ignored) {  
                        }  
      
                    }  
                });  
            }  
            long start = System.currentTimeMillis();  
            startGate.countDown();  
            endGate.await();  
            long end = System.currentTimeMillis();  
            long time = end - start;  
            e.shutdown();  
            System.out.print(time);  
        }  
    @Test
    public void testSingleThread() throws InterruptedException {
        long start = System.currentTimeMillis();  
         while (true) {  
             lock.lock();  
             if (count < MAX*10) {  
                 count++;  
                 lock.unLock();  
                 continue;  
             }  
             lock.unLock();  
             break;  
         }
         long end = System.currentTimeMillis();
         long time = end - start;  
         System.out.print(time);  
    }
    
}

測試結(jié)果淘讥,單線程的執(zhí)行效率最高吉拳,多線程的情況下導(dǎo)致執(zhí)行效率變慢,我的電腦時(shí)4核的适揉,可以見線程的上下文切換很需要時(shí)間,而且CPU容易飆升到100%煤惩,這種情況下反而優(yōu)化后的鎖更浪費(fèi)時(shí)間嫉嘀,因?yàn)镃PU耗盡的原因,所以實(shí)際工作中還是需要結(jié)合服務(wù)器去做測試魄揉。
接下來我們看下著名的CLH鎖和MCS鎖

TAS剪侮、TTAS兩種自旋鎖。這兩種鎖的缺點(diǎn)是:任何一個(gè)處理器每一次對鎖成功的訪問(state.compareAndSet(state.get(),true)和set(false)任意一個(gè)方法的調(diào)用),都會將其他處理器的cache中的緩存失效掉瓣俯。這樣會導(dǎo)致以下后果:
其他處理器無法再采用局部自旋的方式對相同數(shù)據(jù)進(jìn)行訪問杰标,后續(xù)的其他處理器對鎖的訪問都會獨(dú)占bus發(fā)送消息。而鎖的釋放也是需要占用bus彩匕,因此有可能其他處理器對bus的占用而導(dǎo)致鎖的釋放被延遲腔剂;
當(dāng)鎖釋放后的一剎那,其他正在局部自旋的處理器同時(shí)競爭去獲取鎖(調(diào)用state.compareAndSet(state.get(),true))驼仪,原本只有一個(gè)處理器能在此次競爭中獲勝掸犬,但其他獲取失敗的處理器還是在bus上制造了大量無用的交通阻塞,因此也會導(dǎo)致鎖的釋放被延遲绪爸;
由于數(shù)據(jù)訪問存在局部性湾碎,cache從memory中加載數(shù)據(jù)時(shí),一次會加載一個(gè)數(shù)據(jù)塊奠货,即cache存儲的是cache line介褥。Cache中鎖狀態(tài)數(shù)據(jù)的失效,會導(dǎo)致其他和鎖狀態(tài)處于同一個(gè)cache line中的數(shù)據(jù)也被失效掉递惋,從而影響其他原本與鎖不存在競爭的處理器的執(zhí)行速度柔滔。
另外,最后一個(gè)缺點(diǎn)就是無法實(shí)現(xiàn)鎖的公平性丹墨,最先自旋等待獲取鎖的處理器廊遍,在競爭中,不一定最先獲取到鎖贩挣。
雖然基于時(shí)間延遲的BackoffTTAS鎖能一定程度的降低cache被無故失效的次數(shù)喉前,但如何設(shè)置最優(yōu)的延遲時(shí)間卻是一個(gè)難題,因?yàn)椴煌钠脚_會有不同的最優(yōu)參數(shù)王财,同時(shí)卵迂,引入延遲后,有可能導(dǎo)致處理器不能及時(shí)的得到鎖绒净,造成無謂的等待见咒。 那么我們希望最理想的情況是:
由于鎖(此處指排它鎖)同一時(shí)刻只能由一個(gè)處理器持有,所以在鎖釋放后的一剎那挂疆,最多只需要通知一個(gè)正在等待持有鎖的處理器去獲取鎖改览,其他處理器就不需要同時(shí)去湊熱鬧了,輪到它的時(shí)候缤言,會自動(dòng)只通知他宝当。
當(dāng)某個(gè)處理器獲取到鎖或者釋放鎖時(shí),不要失效掉其他還未輪到順序的處理器的cache胆萧,這樣這些處理器就能一直在自己的cache中局部自旋庆揩,不會占用bus。
CLH鎖和MCS鎖就是基于這種理念設(shè)計(jì)出來的

先看CLHLock

package com.alibaba.otter.canal.common;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class CLHLock {
    public static class CLHNode {
        private volatile boolean isLocked = true;
              //CLH算法中,每個(gè)申請鎖的線程通過一個(gè)CLHNode 對象表示订晌,
              //CLHNode 中有一個(gè)**volatile ****boolean**類型的成員變量isLocked 虏辫,
              //isLocked 為true,則表示對應(yīng)的線程已經(jīng)獲取到了鎖或者正在等待獲取鎖锈拨;
              //isLocked 為false砌庄,則表示對應(yīng)的線程已經(jīng)釋放了鎖。
    }
    @SuppressWarnings("unused")
    private volatile CLHNode tail;
    private static final ThreadLocal<CLHNode> LOCAL = new ThreadLocal<CLHNode>();
    private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater
            .newUpdater(CLHLock.class, CLHNode.class, "tail");
    public void lock() {
        CLHNode node = new CLHNode();
        LOCAL.set(node);
        CLHNode preNode = UPDATER.getAndSet(this, node);
        if (preNode != null) {
            while (preNode.isLocked) {
            }
            preNode = null;
            LOCAL.set(node);
        }
    }
    public void unlock() {
        CLHNode node = LOCAL.get();
        if (!UPDATER.compareAndSet(this, node, null)) {
            node.isLocked = false;
        }
        node = null;
    }
}

鎖由多個(gè)CLHNode 對象組成的虛擬鏈表表示推励,之所以稱為虛擬鏈表鹤耍,是因?yàn)镃LHNode 之間并沒有類似于next指針之類的引用,CLHNode 之間通過鎖的一個(gè)本地線程(ThreadLocal)變量LOCAL 相連验辞,preNode 指向當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)稿黄,即當(dāng)前線程的前一個(gè)線程。而鏈表的尾節(jié)點(diǎn)則通過鎖AtomicReferenceFieldUpdater<CLHLock, CLHNode>類型的tail成員變量指向跌造,即tail指向加入到申請鎖的隊(duì)列中的最近一個(gè)線程杆怕。
當(dāng)一個(gè)線程申請鎖時(shí):
首先會實(shí)例化一個(gè)CLHNode 節(jié)點(diǎn),并將其設(shè)置為自己的本地線程變量LOCAL中壳贪;
然后利用 UPDATER.getAndSet(this, node); 得到 前一個(gè)節(jié)點(diǎn) 其實(shí)就是實(shí)例化的 CLHNode 的節(jié)點(diǎn)node 然后在此node上自旋陵珍。
釋放鎖時(shí):
本地變量中獲得node節(jié)點(diǎn) 然后進(jìn)行tail的指向,釋放鎖 然后歸還對象node违施。

再看一下MCSLock

package com.alibaba.otter.canal.common;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class MCSLock {
    public static class MCSNode {
        volatile MCSNode next;
        volatile boolean isLocked = true;
    }
    private static final ThreadLocal<MCSNode>                          NODE    = new ThreadLocal<MCSNode>();
    @SuppressWarnings("unused")
    private volatile MCSNode                                           queue;
    private static final AtomicReferenceFieldUpdater<MCSLock, MCSNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(MCSLock.class,
                                                                                   MCSNode.class, "queue");
    public void lock() {
        MCSNode currentNode = new MCSNode();
        NODE.set(currentNode);
        MCSNode preNode = UPDATER.getAndSet(this, currentNode);
        if (preNode != null) {
            preNode.next = currentNode;
            while (currentNode.isLocked) {

            }
        }
    }
    public void unlock() {
        MCSNode currentNode = NODE.get();
        if (currentNode.next == null) {
            if (UPDATER.compareAndSet(this, currentNode, null)) {

            } else {
                while (currentNode.next == null) {//隊(duì)列尾發(fā)生了改變互纯,必然有新的節(jié)點(diǎn)正在或者將要添加進(jìn)來,因此循環(huán)等待
                }
            }
        } else {
            currentNode.next.isLocked = false;
            currentNode.next = null;
        }
    }
}

MCS隊(duì)列鎖也是為每個(gè)線程分配一個(gè)節(jié)點(diǎn)磕蒲,節(jié)點(diǎn)中任然包含一個(gè)locked屬性留潦,和CLH隊(duì)列鎖不同,MCS隊(duì)列鎖使用一個(gè)真正的隊(duì)列來保存等待線程辣往,因此節(jié)點(diǎn)中還包含一個(gè)next屬性兔院,并且locked屬性的含義也不一樣,在這里表示該線程是否應(yīng)該被阻塞站削,線程將循環(huán)探測自己節(jié)點(diǎn)的locked屬性坊萝,直到該屬性被前續(xù)節(jié)點(diǎn)的線程修改為false。
線程調(diào)用lock后许起,首先獲取自己對應(yīng)的節(jié)點(diǎn)node十偶,并將node設(shè)置為隊(duì)列尾,并返回前續(xù)節(jié)點(diǎn)园细,如果前續(xù)節(jié)點(diǎn)不為空扯键,則表明線程應(yīng)該等待:線程首先將node的locked設(shè)置為true,表示自己將被阻塞珊肃,然后設(shè)置前續(xù)節(jié)點(diǎn)的next指向node,然后就開始循環(huán)直到node的locked屬性被設(shè)置為false。 線程在調(diào)用unlock后伦乔,首先獲取自己對應(yīng)的節(jié)點(diǎn)node厉亏,如果node的next為空,則嘗試將隊(duì)列尾設(shè)置到空烈和,如果成功爱只,則說明隊(duì)列已經(jīng)為空,則退出招刹;否則則說明隊(duì)列尾發(fā)生了變化恬试,需要等待其它線程設(shè)置node的next屬性,最后設(shè)置node的next的locked屬性疯暑,并退出训柴。 MCS隊(duì)列鎖和CLH隊(duì)列鎖具有相同的特點(diǎn),前續(xù)節(jié)點(diǎn)對狀態(tài)的改變只會影響到后續(xù)的節(jié)點(diǎn)妇拯,不同點(diǎn)是MCS隊(duì)列鎖是在本地cache自旋等待幻馁。

總結(jié):我個(gè)人覺得MCSLock更好理解 (不知道為啥會這么想,可能是自旋的自己的節(jié)點(diǎn)的狀態(tài) 而 CLHLock是前驅(qū)節(jié)點(diǎn)的狀態(tài)) 適合MUMA系統(tǒng)越锈,因?yàn)樗鉀Q了CLH在NUMA系統(tǒng)架構(gòu)中獲取locked域狀態(tài)內(nèi)存過遠(yuǎn)的問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仗嗦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子甘凭,更是在濱河造成了極大的恐慌稀拐,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丹弱,死亡現(xiàn)場離奇詭異德撬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹈矮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門砰逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泛鸟,你說我怎么就攤上這事蝠咆。” “怎么了北滥?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵刚操,是天一觀的道長。 經(jīng)常有香客問我再芋,道長菊霜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任济赎,我火速辦了婚禮鉴逞,結(jié)果婚禮上记某,老公的妹妹穿的比我還像新娘。我一直安慰自己构捡,他們只是感情好液南,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著勾徽,像睡著了一般滑凉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喘帚,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天畅姊,我揣著相機(jī)與錄音,去河邊找鬼吹由。 笑死若未,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溉知。 我是一名探鬼主播陨瘩,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼级乍!你這毒婦竟也來了舌劳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤玫荣,失蹤者是張志新(化名)和其女友劉穎甚淡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捅厂,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贯卦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焙贷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撵割。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辙芍,靈堂內(nèi)的尸體忽然破棺而出啡彬,到底是詐尸還是另有隱情,我是刑警寧澤故硅,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布庶灿,位于F島的核電站,受9級特大地震影響吃衅,放射性物質(zhì)發(fā)生泄漏往踢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一徘层、第九天 我趴在偏房一處隱蔽的房頂上張望峻呕。 院中可真熱鬧利职,春花似錦、人聲如沸山上。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佩憾。三九已至,卻和暖如春干花,著一層夾襖步出監(jiān)牢的瞬間妄帘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工池凄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抡驼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓肿仑,卻偏偏與公主長得像致盟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子尤慰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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