深度解析Java 8:JDK1.8 AbstractQueuedSynchronizer的實(shí)現(xiàn)分析

更多多線程API解析請(qǐng)參考jdk8 版本并發(fā)源碼解讀

前言

Java中的FutureTask作為可異步執(zhí)行任務(wù)并可獲取執(zhí)行結(jié)果而被大家所熟知。通晨林ǎ可以使用future.get()來(lái)獲取線程的執(zhí)行結(jié)果酪术,在線程執(zhí)行結(jié)束之前,get方法會(huì)一直阻塞狀態(tài)翠储,直到call()返回绘雁,其優(yōu)點(diǎn)是使用線程異步執(zhí)行任務(wù)的情況下還可以獲取到線程的執(zhí)行結(jié)果,但是FutureTask的以上功能卻是依靠通過(guò)一個(gè)叫AbstractQueuedSynchronizer的類來(lái)實(shí)現(xiàn)援所,至少在JDK 1.5庐舟、JDK1.6版本是這樣的(從1.7開(kāi)始FutureTask已經(jīng)被其作者Doug Lea修改為不再依賴AbstractQueuedSynchronizer實(shí)現(xiàn)了,這是JDK1.7的變化之一)住拭。但是AbstractQueuedSynchronizer在JDK1.8中還有如下圖所示的眾多子類:

這些JDK中的工具類或多或少都被大家用過(guò)不止一次挪略,比如ReentrantLock历帚,我們知道ReentrantLock的功能是實(shí)現(xiàn)代碼段的并發(fā)訪問(wèn)控制,也就是通常意義上所說(shuō)的鎖杠娱,在沒(méi)有看到AbstractQueuedSynchronizer前挽牢,可能會(huì)以為它的實(shí)現(xiàn)是通過(guò)類似于synchronized,通過(guò)對(duì)對(duì)象加鎖來(lái)實(shí)現(xiàn)的墨辛。但事實(shí)上它僅僅是一個(gè)工具類!沒(méi)有使用更“高級(jí)”的機(jī)器指令趴俘,不是關(guān)鍵字睹簇,也不依靠JDK編譯時(shí)的特殊處理,僅僅作為一個(gè)普普通通的類就完成了代碼塊的并發(fā)訪問(wèn)控制寥闪,這就更讓人疑問(wèn)它怎么實(shí)現(xiàn)的代碼塊的并發(fā)訪問(wèn)控制的了太惠。那就讓我們一起來(lái)仔細(xì)看下Doug Lea怎么去實(shí)現(xiàn)的這個(gè)鎖。為了方便疲憋,本文中使用AQS代替AbstractQueuedSynchronizer凿渊。

細(xì)說(shuō)AQS

在深入分析AQS之前,我想先從AQS的功能上說(shuō)明下AQS缚柳,站在使用者的角度埃脏,AQS的功能可以分為兩類:獨(dú)占功能和共享功能,它的所有子類中秋忙,要么實(shí)現(xiàn)并使用了它獨(dú)占功能的API彩掐,要么使用了共享鎖的功能,而不會(huì)同時(shí)使用兩套API灰追,即便是它最有名的子類ReentrantReadWriteLock堵幽,也是通過(guò)兩個(gè)內(nèi)部類:讀鎖和寫鎖,分別實(shí)現(xiàn)的兩套API來(lái)實(shí)現(xiàn)的弹澎,為什么這么做朴下,后面我們?cè)俜治觯侥壳盀橹箍噍铮覀冎恍枰靼譇QS在功能上有獨(dú)占控制和共享控制兩種功能即可殴胧。

獨(dú)占鎖

在真正對(duì)解讀AQS之前,我想先從使用了它獨(dú)占控制功能的子類ReentrantLock說(shuō)起佩迟,分析ReentrantLock的同時(shí)看一看AQS的實(shí)現(xiàn)溃肪,再推理出AQS獨(dú)特的設(shè)計(jì)思路和實(shí)現(xiàn)方式。最后音五,再看其共享控制功能的實(shí)現(xiàn)惫撰。

對(duì)于ReentrantLock,使用過(guò)的同學(xué)應(yīng)該都知道躺涝,通常是這么用它的:

reentrantLock.lock()
        //do something
        reentrantLock.unlock()

ReentrantLock會(huì)保證 do something在同一時(shí)間只有一個(gè)線程在執(zhí)行這段代碼厨钻,或者說(shuō)扼雏,同一時(shí)刻只有一個(gè)線程的lock方法會(huì)返回。其余線程會(huì)被掛起夯膀,直到獲取鎖诗充。從這里可以看出,其實(shí)ReentrantLock實(shí)現(xiàn)的就是一個(gè)獨(dú)占鎖的功能:有且只有一個(gè)線程獲取到鎖诱建,其余線程全部掛起蝴蜓,直到該擁有鎖的線程釋放鎖,被掛起的線程被喚醒重新開(kāi)始競(jìng)爭(zhēng)鎖俺猿。沒(méi)錯(cuò)茎匠,ReentrantLock使用的就是AQS的獨(dú)占API實(shí)現(xiàn)的。

那現(xiàn)在我們就從ReentrantLock的實(shí)現(xiàn)開(kāi)始一起看看重入鎖是怎么實(shí)現(xiàn)的押袍。

首先看lock方法:

如FutureTask(JDK1.6)一樣诵冒,ReentrantLock內(nèi)部有代理類完成具體操作,ReentrantLock只是封裝了統(tǒng)一的一套API而已谊惭。值得注意的是汽馋,使用過(guò)ReentrantLock的同學(xué)應(yīng)該知道,ReentrantLock又分為公平鎖和非公平鎖圈盔,所以豹芯,ReentrantLock內(nèi)部只有兩個(gè)sync的實(shí)現(xiàn):

公平鎖:每個(gè)線程搶占鎖的順序?yàn)橄群笳{(diào)用lock方法的順序依次獲取鎖,類似于排隊(duì)吃飯驱敲。

非公平鎖:每個(gè)線程搶占鎖的順序不定告组,誰(shuí)運(yùn)氣好,誰(shuí)就獲取到鎖癌佩,和調(diào)用lock方法的先后順序無(wú)關(guān)木缝,類似于堵車時(shí),加塞的那些XXXX围辙。

到這里我碟,通過(guò)ReentrantLock的功能和鎖的所謂排不排隊(duì)的方式,我們是否可以這么猜測(cè)ReentrantLock或者AQS的實(shí)現(xiàn)(現(xiàn)在不清楚誰(shuí)去實(shí)現(xiàn)這些功能):有那么一個(gè)被volatile修飾的標(biāo)志位叫做key姚建,用來(lái)表示有沒(méi)有線程拿走了鎖矫俺,或者說(shuō),鎖還存不存在掸冤,還需要一個(gè)線程安全的隊(duì)列厘托,維護(hù)一堆被掛起的線程,以至于當(dāng)鎖被歸還時(shí)稿湿,能通知到這些被掛起的線程铅匹,可以來(lái)競(jìng)爭(zhēng)獲取鎖了。

至于公平鎖和非公平鎖饺藤,唯一的區(qū)別是在獲取鎖的時(shí)候是直接去獲取鎖包斑,還是進(jìn)入隊(duì)列排隊(duì)的問(wèn)題了流礁。為了驗(yàn)證我們的猜想,我們繼續(xù)看一下ReentrantLock中公平鎖的實(shí)現(xiàn):

調(diào)用到了AQS的acquire方法:

從方法名字上看語(yǔ)義是罗丰,嘗試獲取鎖神帅,獲取不到則創(chuàng)建一個(gè)waiter(當(dāng)前線程)后放到隊(duì)列中,這和我們猜測(cè)的好像很類似萌抵。

先看下tryAcquire方法:

留空了找御,Doug Lea是想留給子類去實(shí)現(xiàn)(既然要給子類實(shí)現(xiàn),應(yīng)該用抽象方法绍填,但是Doug Lea沒(méi)有這么做霎桅,原因是AQS有兩種功能,面向兩種使用場(chǎng)景沐兰,需要給子類定義的方法都是抽象方法了哆档,會(huì)導(dǎo)致子類無(wú)論如何都需要實(shí)現(xiàn)另外一種場(chǎng)景的抽象方法蔽挠,顯然住闯,這對(duì)子類來(lái)說(shuō)是不友好的。)

看下FairSync的tryAcquire方法:

getState方法是AQS的方法澳淑,因?yàn)樵贏QS里面有個(gè)叫statede的標(biāo)志位 :

事實(shí)上比原,這個(gè)state就是前面我們猜想的那個(gè)“key”!

回到tryAcquire方法:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();//獲取當(dāng)前線程
            int c = getState();  //獲取父類AQS中的標(biāo)志位
            if (c == 0) {
                if (!hasQueuedPredecessors() && 
                    //如果隊(duì)列中沒(méi)有其他線程  說(shuō)明沒(méi)有線程正在占有鎖杠巡!
                    compareAndSetState(0, acquires)) { 
                    //修改一下?tīng)顟B(tài)位量窘,注意:這里的acquires是在lock的時(shí)候傳遞來(lái)的,從上面的圖中可以知道氢拥,這個(gè)值是寫死的1
                    setExclusiveOwnerThread(current);
                    //如果通過(guò)CAS操作將狀態(tài)為更新成功則代表當(dāng)前線程獲取鎖蚌铜,因此,將當(dāng)前線程設(shè)置到AQS的一個(gè)變量中嫩海,說(shuō)明這個(gè)線程拿走了鎖冬殃。
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
             //如果不為0 意味著,鎖已經(jīng)被拿走了,但是,因?yàn)镽eentrantLock是重入鎖岳枷,
             //是可以重復(fù)lock,unlock的稳强,只要成對(duì)出現(xiàn)行。一次斯稳。這里還要再判斷一次 獲取鎖的線程是不是當(dāng)前請(qǐng)求鎖的線程。
                int nextc = c + acquires;//如果是的,累加在state字段上就可以了官册。
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

到此,如果如果獲取鎖难捌,tryAcquire返回true攀隔,反之皂贩,返回false,回到AQS的acquire方法昆汹。

如果沒(méi)有獲取到鎖明刷,按照我們的描述,應(yīng)該將當(dāng)前線程放到隊(duì)列中去满粗,只不過(guò)辈末,在放之前,需要做些包裝映皆。

先看addWaiter方法:

用當(dāng)前線程去構(gòu)造一個(gè)Node對(duì)象挤聘,mode是一個(gè)表示Node類型的字段,僅僅表示這個(gè)節(jié)點(diǎn)是獨(dú)占的捅彻,還是共享的组去,或者說(shuō),AQS的這個(gè)隊(duì)列中步淹,哪些節(jié)點(diǎn)是獨(dú)占的从隆,哪些是共享的。

這里lock調(diào)用的是AQS獨(dú)占的API缭裆,當(dāng)然键闺,可以寫死是獨(dú)占狀態(tài)的節(jié)點(diǎn)。

創(chuàng)建好節(jié)點(diǎn)后澈驼,將節(jié)點(diǎn)加入到隊(duì)列尾部辛燥,此處,在隊(duì)列不為空的時(shí)候缝其,先嘗試通過(guò)cas方式修改尾節(jié)點(diǎn)為最新的節(jié)點(diǎn)挎塌,如果修改失敗,意味著有并發(fā)内边,這個(gè)時(shí)候才會(huì)進(jìn)入enq中死循環(huán)榴都,“自旋”方式修改。

將線程的節(jié)點(diǎn)接入到隊(duì)里中后假残,當(dāng)然還需要做一件事:將當(dāng)前線程掛起缭贡!這個(gè)事,由acquireQueued來(lái)做辉懒。

在解釋acquireQueued之前阳惹,我們需要先看下AQS中隊(duì)列的內(nèi)存結(jié)構(gòu),我們知道眶俩,隊(duì)列由Node類型的節(jié)點(diǎn)組成莹汤,其中至少有兩個(gè)變量,一個(gè)封裝線程颠印,一個(gè)封裝節(jié)點(diǎn)類型纲岭。

而實(shí)際上抹竹,它的內(nèi)存結(jié)構(gòu)是這樣的(第一次節(jié)點(diǎn)插入時(shí),第一個(gè)節(jié)點(diǎn)是一個(gè)空節(jié)點(diǎn)止潮,代表有一個(gè)線程已經(jīng)獲取鎖窃判,事實(shí)上,隊(duì)列的第一個(gè)節(jié)點(diǎn)就是代表持有鎖的節(jié)點(diǎn)):

黃色節(jié)點(diǎn)為隊(duì)列默認(rèn)的頭節(jié)點(diǎn)喇闸,每次有線程競(jìng)爭(zhēng)失敗袄琳,進(jìn)入隊(duì)列后其實(shí)都是插入到隊(duì)列的尾節(jié)點(diǎn)(tail后面)后面。這個(gè)從enq方法可以看出來(lái)燃乍,上文中有提到enq方法為將節(jié)點(diǎn)插入隊(duì)列的方法:

再回來(lái)看看

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
             //如果當(dāng)前的節(jié)點(diǎn)是head說(shuō)明他是隊(duì)列中第一個(gè)“有效的”節(jié)點(diǎn)唆樊,因此嘗試獲取,上文中有提到這個(gè)類是交給子類去擴(kuò)展的刻蟹。
                    setHead(node);//成功后逗旁,將上圖中的黃色節(jié)點(diǎn)移除,Node1變成頭節(jié)點(diǎn)舆瘪。
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && 
                //否則片效,檢查前一個(gè)節(jié)點(diǎn)的狀態(tài)為,看當(dāng)前獲取鎖失敗的線程是否需要掛起介陶。
                    parkAndCheckInterrupt()) 
               //如果需要堤舒,借助JUC包下的LockSopport類的靜態(tài)方法Park掛起當(dāng)前線程色建。知道被喚醒哺呜。
                    interrupted = true;
            }
        } finally {
            if (failed) //如果有異常
                cancelAcquire(node);// 取消請(qǐng)求,對(duì)應(yīng)到隊(duì)列操作箕戳,就是將當(dāng)前節(jié)點(diǎn)從隊(duì)列中移除某残。
        }
    }

這塊代碼有幾點(diǎn)需要說(shuō)明:

  1. Node節(jié)點(diǎn)中,除了存儲(chǔ)當(dāng)前線程陵吸,節(jié)點(diǎn)類型玻墅,隊(duì)列中前后元素的變量,還有一個(gè)叫waitStatus的變量壮虫,改變量用于描述節(jié)點(diǎn)的狀態(tài)澳厢,為什么需要這個(gè)狀態(tài)呢?
    原因是:AQS的隊(duì)列中囚似,在有并發(fā)時(shí)剩拢,肯定會(huì)存取一定數(shù)量的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)代表了一個(gè)線程的狀態(tài)饶唤,有的線程可能“等不及”獲取鎖了徐伐,需要放棄競(jìng)爭(zhēng),退出隊(duì)列募狂,有的線程在等待一些條件滿足办素,滿足后才恢復(fù)執(zhí)行(這里的描述很像某個(gè)J.U.C包下的工具類角雷,ReentrankLock的Condition,事實(shí)上性穿,Condition同樣也是AQS的子類)等等勺三,總之,各個(gè)線程有各個(gè)線程的狀態(tài)需曾,但總需要一個(gè)變量來(lái)描述它檩咱,這個(gè)變量就叫waitStatus,它有四種狀態(tài):

分別表示:

節(jié)點(diǎn)取消
節(jié)點(diǎn)等待觸發(fā)
節(jié)點(diǎn)等待條件
節(jié)點(diǎn)狀態(tài)需要向后傳播。
只有當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)為SIGNAL時(shí)胯舷,才能當(dāng)前節(jié)點(diǎn)才能被掛起刻蚯。

對(duì)線程的掛起及喚醒操作是通過(guò)使用UNSAFE類調(diào)用JNI方法實(shí)現(xiàn)的。當(dāng)然桑嘶,還提供了掛起指定時(shí)間后喚醒的API炊汹,在后面我們會(huì)講到。

到此為止逃顶,一個(gè)線程對(duì)于鎖的一次競(jìng)爭(zhēng)才告于段落讨便,結(jié)果有兩種,要么成功獲取到鎖(不用進(jìn)入到AQS隊(duì)列中)以政,要么霸褒,獲取失敗,被掛起盈蛮,等待下次喚醒后繼續(xù)循環(huán)嘗試獲取鎖废菱,值得注意的是,AQS的隊(duì)列為FIFO隊(duì)列抖誉,所以殊轴,每次被CPU假喚醒,且當(dāng)前線程不是出在頭節(jié)點(diǎn)的位置袒炉,也是會(huì)被掛起的旁理。AQS通過(guò)這樣的方式,實(shí)現(xiàn)了競(jìng)爭(zhēng)的排隊(duì)策略我磁。

看完了獲取鎖孽文,在看看釋放鎖,具體看代碼之前夺艰,我們可以先繼續(xù)猜下芋哭,釋放操作需要做哪些事情:

因?yàn)楂@取鎖的線程的節(jié)點(diǎn),此時(shí)在AQS的頭節(jié)點(diǎn)位置劲适,所以楷掉,可能需要將頭節(jié)點(diǎn)移除。
而應(yīng)該是直接釋放鎖,然后找到AQS的頭節(jié)點(diǎn)烹植,通知它可以來(lái)競(jìng)爭(zhēng)鎖了斑鸦。
是不是這樣呢?我們繼續(xù)來(lái)看下,同樣我們用ReentrantLock的FairSync來(lái)說(shuō)明:

unlock方法調(diào)用了AQS的release方法草雕,同樣傳入了參數(shù)1巷屿,和獲取鎖的相應(yīng)對(duì)應(yīng),獲取一個(gè)鎖墩虹,標(biāo)示為+1嘱巾,釋放一個(gè)鎖,標(biāo)志位-1诫钓。

同樣旬昭,release為空方法,子類自己實(shí)現(xiàn)邏輯:

protected final boolean tryRelease(int releases) {
       int c = getState() - releases; 
       if (Thread.currentThread() != getExclusiveOwnerThread()) //如果釋放的線程和獲取鎖的線程不是同一個(gè)菌湃,拋出非法監(jiān)視器狀         態(tài)異常问拘。
           throw new IllegalMonitorStateException();
       boolean free = false;
       if (c == 0) {//因?yàn)槭侵厝氲年P(guān)系,不是每次釋放鎖c都等于0惧所,直到最后一次釋放鎖時(shí)骤坐,才通知AQS不需要再記錄哪個(gè)線程正在獲取鎖。
           free = true;
           setExclusiveOwnerThread(null);
       }
       setState(c);
       return free;
   }

釋放鎖下愈,成功后纽绍,找到AQS的頭節(jié)點(diǎn),并喚醒它即可:

值得注意的是势似,尋找的順序是從隊(duì)列尾部開(kāi)始往前去找的最前面的一個(gè)waitStatus小于0的節(jié)點(diǎn)拌夏。

到此,ReentrantLock的lock和unlock方法已經(jīng)基本解析完畢了叫编,唯獨(dú)還剩下一個(gè)非公平鎖NonfairSync沒(méi)說(shuō)辖佣,其實(shí)霹抛,它和公平鎖的唯一區(qū)別就是獲取鎖的方式不同搓逾,一個(gè)是按前后順序一次獲取鎖,一個(gè)是搶占式的獲取鎖杯拐,那ReentrantLock是怎么實(shí)現(xiàn)的呢霞篡?再看兩段代碼:

非公平鎖的lock方法的處理方式是: 在lock的時(shí)候先直接cas修改一次state變量(嘗試獲取鎖),成功就返回端逼,不成功再排隊(duì)朗兵,從而達(dá)到不排隊(duì)直接搶占的目的。

而對(duì)于公平鎖:則是老老實(shí)實(shí)的開(kāi)始就走AQS的流程排隊(duì)獲取鎖顶滩。如果前面有人調(diào)用過(guò)其lock方法余掖,則排在隊(duì)列中前面,也就更有機(jī)會(huì)更早的獲取鎖礁鲁,從而達(dá)到“公平”的目的盐欺。

總結(jié)

這篇文章赁豆,我們從ReentrantLock出發(fā),完整的分析了AQS獨(dú)占功能的API及內(nèi)部實(shí)現(xiàn)冗美,總的來(lái)說(shuō)魔种,思路其實(shí)并不復(fù)雜,還是使用的標(biāo)志位+隊(duì)列的方式粉洼,記錄獲取鎖节预、競(jìng)爭(zhēng)鎖、釋放鎖等一系列鎖的狀態(tài)属韧,或許用更準(zhǔn)確一點(diǎn)的描述的話安拟,應(yīng)該是使用的標(biāo)志位+隊(duì)列的方式,記錄鎖宵喂、競(jìng)爭(zhēng)去扣、釋放等一系列獨(dú)占的狀態(tài),因?yàn)檎驹贏QS的層面state可以表示鎖樊破,也可以表示其他狀態(tài)愉棱,它并不關(guān)心它的子類把它變成一個(gè)什么工具類,而只是提供了一套維護(hù)一個(gè)獨(dú)占狀態(tài)哲戚。甚至奔滑,最準(zhǔn)確的是AQS只是維護(hù)了一個(gè)狀態(tài),因?yàn)樗成伲瑒e忘了朋其,它還有一套共享狀態(tài)的API,所以脆炎,AQS只是維護(hù)一個(gè)狀態(tài)梅猿,一個(gè)控制各個(gè)線程何時(shí)可以訪問(wèn)的狀態(tài),它只對(duì)狀態(tài)負(fù)責(zé)秒裕,而這個(gè)狀態(tài)表示什么含義袱蚓,由子類自己去定義。

極樂(lè)科技知乎專欄:深度解析Java 8:JDK1.8 AbstractQueuedSynchronizer的實(shí)現(xiàn)分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末几蜻,一起剝皮案震驚了整個(gè)濱河市喇潘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梭稚,老刑警劉巖颖低,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異弧烤,居然都是意外死亡忱屑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)莺戒,“玉大人粱栖,你說(shuō)我怎么就攤上這事≡嗵海” “怎么了闹究?”我有些...
    開(kāi)封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)食店。 經(jīng)常有香客問(wèn)我渣淤,道長(zhǎng),這世上最難降的妖魔是什么吉嫩? 我笑而不...
    開(kāi)封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任价认,我火速辦了婚禮,結(jié)果婚禮上自娩,老公的妹妹穿的比我還像新娘用踩。我一直安慰自己,他們只是感情好忙迁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布脐彩。 她就那樣靜靜地躺著,像睡著了一般姊扔。 火紅的嫁衣襯著肌膚如雪惠奸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天恰梢,我揣著相機(jī)與錄音佛南,去河邊找鬼。 笑死嵌言,一個(gè)胖子當(dāng)著我的面吹牛嗅回,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摧茴,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绵载,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蓬蝶?” 一聲冷哼從身側(cè)響起尘分,我...
    開(kāi)封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丸氛,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體著摔,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缓窜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禾锤。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡私股,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恩掷,到底是詐尸還是另有隱情倡鲸,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布黄娘,位于F島的核電站峭状,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏逼争。R本人自食惡果不足惜优床,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望誓焦。 院中可真熱鬧胆敞,春花似錦、人聲如沸杂伟。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赫粥。三九已至幽钢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傅是,已是汗流浹背匪燕。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喧笔,地道東北人帽驯。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像书闸,于是被迫代替她去往敵國(guó)和親尼变。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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