Java并發(fā)之AQS源碼分析(一)

AQS 全稱是 AbstractQueuedSynchronizer,顧名思義,是一個(gè)用來(lái)構(gòu)建鎖和同步器的框架,它底層用了 CAS 技術(shù)來(lái)保證操作的原子性,同時(shí)利用 FIFO 隊(duì)列實(shí)現(xiàn)線程間的鎖競(jìng)爭(zhēng),將基礎(chǔ)的同步相關(guān)抽象細(xì)節(jié)放在 AQS,這也是 ReentrantLock、CountDownLatch 等同步工具實(shí)現(xiàn)同步的底層實(shí)現(xiàn)機(jī)制。它能夠成為實(shí)現(xiàn)大部分同步需求的基礎(chǔ),也是 J.U.C 并發(fā)包同步的核心基礎(chǔ)組件。

AQS 結(jié)構(gòu)剖析

AQS 就是建立在 CAS 的基礎(chǔ)之上,增加了大量的實(shí)現(xiàn)細(xì)節(jié),例如獲取同步狀態(tài)、FIFO 同步隊(duì)列,獨(dú)占式鎖和共享式鎖的獲取和釋放等等,這些都是 AQS 類對(duì)于同步操作抽離出來(lái)的一些通用方法,這么做也是為了對(duì)實(shí)現(xiàn)的一個(gè)同步類屏蔽了大量的細(xì)節(jié),大大降低了實(shí)現(xiàn)同步工具的工作量,這也是為什么 AQS 是其它許多同步類的基類的原因。

現(xiàn)在我們來(lái)直接定位到類 java.util.concurrent.locks.AbstractQueuedSynchronizer,下面是 AQS 類的幾個(gè)重要字段與方法列出來(lái):

publicabstractclassAbstractQueuedSynchronizerextendsAbstractOwnableSynchronizerimplementsjava.io.Serializable{privatetransientvolatileNode head;privatetransientvolatileNode tail;privatevolatileintstate;protectedfinalbooleancompareAndSetState(intexpect,intupdate){// See below for intrinsics setup to support thisreturnunsafe.compareAndSwapInt(this, stateOffset, expect, update);? ? }// ...}

head 字段為等待隊(duì)列的頭節(jié)點(diǎn),表示當(dāng)前正在執(zhí)行的節(jié)點(diǎn);

tail 字段為等待隊(duì)列的尾節(jié)點(diǎn);

state 字段為同步狀態(tài),其中 state > 0 為有鎖狀態(tài),每次加鎖就在原有 state 基礎(chǔ)上加 1,即代表當(dāng)前持有鎖的線程加了 state 次鎖,反之解鎖時(shí)每次減一,當(dāng) statte = 0 為無(wú)鎖狀態(tài);

通過(guò) compareAndSetState 方法操作 CAS 更改 state 狀態(tài),保證 state 的原子性。

有沒(méi)有發(fā)現(xiàn),這幾個(gè)字段都用 volatile 關(guān)鍵字進(jìn)行修飾,以確保多線程間保證字段的可見(jiàn)性。

AQS 提供了兩種鎖,分別是獨(dú)占鎖和共享鎖,獨(dú)占鎖指的是操作被認(rèn)作一種獨(dú)占操作,比如 ReentrantLock,它實(shí)現(xiàn)了獨(dú)占鎖的方法,而共享鎖則指的是一個(gè)非獨(dú)占操作,比如一些同步工具 CountDownLatch 和 Semaphore 等同步工具,下面是 AQS 對(duì)這兩種鎖提供的抽象方法。

獨(dú)占鎖:

// 獲取鎖方法protectedbooleantryAcquire(intarg){thrownewUnsupportedOperationException();}// 釋放鎖方法protectedbooleantryRelease(intarg){thrownewUnsupportedOperationException();}

共享鎖:

// 獲取鎖方法protectedinttryAcquireShared(intarg){thrownewUnsupportedOperationException();}// 釋放鎖方法protectedbooleantryReleaseShared(intarg){thrownewUnsupportedOperationException();}

在我們平時(shí)開(kāi)發(fā)中,基本不用直接使用 AQS,我們平時(shí)都是直接使用 JDK 自帶的同步類工具,如 ReentrantLock、CountDownLatch 和 Semaphore 等,它們已經(jīng)可以滿足絕大部分的需求了,后面會(huì)抽幾篇文章單獨(dú)講一下這些同步類工具是如何使用 AQS 的,這對(duì)于我們?nèi)绾螛?gòu)建自定義的同步工具,有很大的幫助。

下面是同步隊(duì)列節(jié)點(diǎn)的結(jié)構(gòu):

用大神的注釋來(lái)形象地描述一下隊(duì)列的模型:

/**? *

? *? ? ? +------+? prev +-----+? ? ?  +-----+* head |? ? ? | <---- |? ?  | <---- |? ?  |? tail*? ? ? +------+? ? ?  +-----+? ? ?  +-----+* 

? */

這是一個(gè)普通雙向鏈表的節(jié)點(diǎn)結(jié)構(gòu),多了 thread 字段用于存儲(chǔ)當(dāng)前線程對(duì)象,同時(shí)每個(gè)節(jié)點(diǎn)都有一個(gè) waitStatus 等待狀態(tài),一共有四種狀態(tài):

CANCELLED(1):取消狀態(tài),如果當(dāng)前線程的前置節(jié)點(diǎn)狀態(tài)為 CANCELLED,則表明前置節(jié)點(diǎn)已經(jīng)等待超時(shí)或者已經(jīng)被中斷了,這時(shí)需要將其從等待隊(duì)列中刪除。

SIGNAL(-1):等待觸發(fā)狀態(tài),如果當(dāng)前線程的前置節(jié)點(diǎn)狀態(tài)為 SIGNAL,則表明當(dāng)前線程需要阻塞。

CONDITION(-2):等待條件狀態(tài),表示當(dāng)前節(jié)點(diǎn)在等待 condition,即在 condition 隊(duì)列中。

PROPAGATE(-3):狀態(tài)需要向后傳播,表示 releaseShared 需要被傳播給后續(xù)節(jié)點(diǎn),僅在共享鎖模式下使用。

可以這么理解:head 節(jié)點(diǎn)可以表示成當(dāng)前持有鎖的線程的節(jié)點(diǎn),其余線程競(jìng)爭(zhēng)鎖失敗后,會(huì)加入到隊(duì)尾,tail 始終指向隊(duì)列的最后一個(gè)節(jié)點(diǎn)。

AQS 的結(jié)構(gòu)大概可總結(jié)為以下 3 部分:

用 volatile 修飾的整數(shù)類型的 state 狀態(tài),用于表示同步狀態(tài),提供 getState 和 setState 來(lái)操作同步狀態(tài);

提供了一個(gè) FIFO 等待隊(duì)列,實(shí)現(xiàn)線程間的競(jìng)爭(zhēng)和等待,這是 AQS 的核心;

AQS 內(nèi)部提供了各種基于 CAS 原子操作方法,如 compareAndSetState 方法,并且提供了鎖操作的acquire和release方法。

獨(dú)占鎖

獨(dú)占鎖的原理是如果有線程獲取到鎖,那么其它線程只能是獲取鎖失敗,然后進(jìn)入等待隊(duì)列中等待被喚醒。

進(jìn)群:697699179可以獲取Java各類入門(mén)學(xué)習(xí)資料!

這是我的微信公眾號(hào)【編程study】各位大佬有空可以關(guān)注下,每天更新Java學(xué)習(xí)方法,感謝!

學(xué)習(xí)中遇到問(wèn)題有不明白的地方,推薦加小編Java學(xué)習(xí)群:697699179內(nèi)有視頻教程 ,直播課程 ,等學(xué)習(xí)資料,期待你的加入

獲取鎖

獲取獨(dú)占鎖方法:

publicfinalvoidacquire(intarg){if(!tryAcquire(arg) &&? ? ? acquireQueued(addWaiter(Node.EXCLUSIVE), arg))? ? selfInterrupt();}

源碼解讀:

通過(guò) tryAcquire(arg) 方法嘗試獲取鎖,這個(gè)方法需要實(shí)現(xiàn)類自己實(shí)現(xiàn)獲取鎖的邏輯,獲取鎖成功后則不執(zhí)行后面加入等待隊(duì)列的邏輯了;

如果嘗試獲取鎖失敗后,則執(zhí)行 addWaiter(Node.EXCLUSIVE) 方法將當(dāng)前線程封裝成一個(gè) Node 節(jié)點(diǎn)對(duì)象,并加入隊(duì)列尾部;

把當(dāng)前線程執(zhí)行封裝成 Node 節(jié)點(diǎn)后,繼續(xù)執(zhí)行 acquireQueued 的邏輯,該邏輯主要是判斷當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)是否是頭節(jié)點(diǎn),來(lái)嘗試獲取鎖,如果獲取鎖成功,則當(dāng)前節(jié)點(diǎn)就會(huì)成為新的頭節(jié)點(diǎn),這也是獲取鎖的核心邏輯。

基于上面源碼的步驟分析后,我們一步步往下看源碼具體實(shí)現(xiàn):

privateNode addWaiter(Node mode) {// 創(chuàng)建一個(gè)基于當(dāng)前線程的節(jié)點(diǎn),該節(jié)點(diǎn)是 Node.EXCLUSIVE 獨(dú)占式類型Node node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred = tail;// 這里先判斷隊(duì)尾是否為空,如果不為空則直接將節(jié)點(diǎn)加入隊(duì)尾if(pred !=null) {? ? node.prev = pred;// 采取 CAS 操作,將當(dāng)前節(jié)點(diǎn)設(shè)置為隊(duì)尾節(jié)點(diǎn),由于采用了 CAS 原子操作,無(wú)論并發(fā)怎么修改,都有且只有一條線程可以修改成功,其余都將執(zhí)行后面的enq方法if(compareAndSetTail(pred, node)) {? ? ? pred.next = node;returnnode;? ? }? }? enq(node);returnnode;}

簡(jiǎn)單來(lái)說(shuō) addWaiter(Node mode) 方法做了以下事情:

創(chuàng)建基于當(dāng)前線程的獨(dú)占式類型的節(jié)點(diǎn);

利用 CAS 原子操作,將節(jié)點(diǎn)加入隊(duì)尾。

我們繼續(xù)看 enq(Node node) 方法:

privateNodeenq(finalNode node){// 自旋操作for(;;) {? ? Node t = tail;// 如果隊(duì)尾節(jié)點(diǎn)為空,那么進(jìn)行CAS操作初始化隊(duì)列if(t ==null) {// 這里很關(guān)鍵,即如果隊(duì)列為空,那么此時(shí)必須初始化隊(duì)列,初始化一個(gè)空的節(jié)點(diǎn)表示隊(duì)列頭,用于表示當(dāng)前正在執(zhí)行的節(jié)點(diǎn),頭節(jié)點(diǎn)即表示當(dāng)前正在運(yùn)行的節(jié)點(diǎn)if(compareAndSetHead(newNode()))? ? ? ? tail = head;? ? }else{? ? ? node.prev = t;// 這一步也是采取CAS操作,將當(dāng)前節(jié)點(diǎn)加入隊(duì)尾,如果失敗的話,自旋繼續(xù)修改直到成功為止if(compareAndSetTail(t, node)) {? ? ? ? t.next = node;returnt;? ? ? }? ? }? }}

enq(final Node node) 方法主要做了以下事情:

采用自旋機(jī)制,這是 aqs 里面很重要的一個(gè)機(jī)制;

如果隊(duì)尾節(jié)點(diǎn)為空,則初始化隊(duì)列,將頭節(jié)點(diǎn)設(shè)置為空節(jié)點(diǎn),頭節(jié)點(diǎn)即表示當(dāng)前正在運(yùn)行的節(jié)點(diǎn);

如果隊(duì)尾節(jié)點(diǎn)不為空,則繼續(xù)采取 CAS 操作,將當(dāng)前節(jié)點(diǎn)加入隊(duì)尾,不成功則繼續(xù)自旋,直到成功為止;

對(duì)比了上面兩段代碼,不難看出,首先是判斷隊(duì)尾是否為空,先進(jìn)行一次 CAS 入隊(duì)操作,如果失敗則進(jìn)入 enq(final Node node) 方法執(zhí)行完整的入隊(duì)操作。

完整的入隊(duì)操作簡(jiǎn)單來(lái)說(shuō)就是:?如果隊(duì)列為空,初始化隊(duì)列,并將頭節(jié)點(diǎn)設(shè)為空節(jié)點(diǎn),表示當(dāng)前正在運(yùn)行的節(jié)點(diǎn),然后再將當(dāng)前線程的節(jié)點(diǎn)加入到隊(duì)列尾部。

關(guān)于隊(duì)列的初始化與入隊(duì),務(wù)必理解透徹。

經(jīng)過(guò)上面 CAS 不斷嘗試,這時(shí)當(dāng)前節(jié)點(diǎn)已經(jīng)成功加入到隊(duì)尾了,接下來(lái)就到了acquireQueued 的邏輯,我們繼續(xù)往下看源碼:

finalbooleanacquireQueued(finalNode node,intarg){booleanfailed =true;try{// 線程中斷標(biāo)記字段booleaninterrupted =false;for(;;) {// 獲取當(dāng)前節(jié)點(diǎn)的 pred 節(jié)點(diǎn)finalNode p = node.predecessor();// 如果 pred 節(jié)點(diǎn)為 head 節(jié)點(diǎn),那么再次嘗試獲取鎖if(p == head && tryAcquire(arg)) {// 獲取鎖之后,那么當(dāng)前節(jié)點(diǎn)也就成為了 head 節(jié)點(diǎn)setHead(node);? ? ? ? p.next =null;// help GCfailed =false;// 不需要掛起,返回 falsereturninterrupted;? ? ? }// 獲取鎖失敗,則進(jìn)入掛起邏輯if(shouldParkAfterFailedAcquire(p, node) &&? ? ? ? ? parkAndCheckInterrupt())? ? ? ? interrupted =true;? ? }? }finally{if(failed)? ? ? cancelAcquire(node);? }}

這一步 acquireQueued(final Node node, int arg) 方法主要做了以下事情:

判斷當(dāng)前節(jié)點(diǎn)的 pred 節(jié)點(diǎn)是否為 head 節(jié)點(diǎn),如果是,則嘗試獲取鎖;

獲取鎖失敗后,進(jìn)入掛起邏輯。

提醒一點(diǎn):?我們上面也說(shuō)過(guò),head 節(jié)點(diǎn)代表當(dāng)前持有鎖的線程,那么如果當(dāng)前節(jié)點(diǎn)的 pred 節(jié)點(diǎn)是 head 節(jié)點(diǎn),很可能此時(shí) head 節(jié)點(diǎn)已經(jīng)釋放鎖了,所以此時(shí)需要再次嘗試獲取鎖。

接下來(lái)繼續(xù)看掛起邏輯源碼:

privatestaticbooleanshouldParkAfterFailedAcquire(Node pred, Node node) {intws = pred.waitStatus;if(ws == Node.SIGNAL)// 如果 pred 節(jié)點(diǎn)為 SIGNAL 狀態(tài),返回true,說(shuō)明當(dāng)前節(jié)點(diǎn)需要掛起returntrue;// 如果ws > 0,說(shuō)明節(jié)點(diǎn)狀態(tài)為CANCELLED,需要從隊(duì)列中刪除if(ws >0) {do{? ? ? node.prev = pred = pred.prev;? ? }while(pred.waitStatus >0);? ? pred.next= node;? }else{// 如果是其它狀態(tài),則操作CAS統(tǒng)一改成SIGNAL狀態(tài)// 由于這里waitStatus的值只能是0或者PROPAGATE,所以我們將節(jié)點(diǎn)設(shè)置為SIGNAL,從新循環(huán)一次判斷compareAndSetWaitStatus(pred, ws, Node.SIGNAL);? }returnfalse;}

這一步 shouldParkAfterFailedAcquire(Node pred, Node node) 方法主要做了以下事情:

判斷 pred 節(jié)點(diǎn)狀態(tài),如果為 SIGNAL 狀態(tài),則直接返回 true 執(zhí)行掛起;

刪除狀態(tài)為 CANCELLED 的節(jié)點(diǎn);

若 pred 節(jié)點(diǎn)狀態(tài)為 0 或者 PROPAGATE,則將其設(shè)置為為 SIGNAL,再?gòu)?acquireQueued 方法自旋操作從新循環(huán)一次判斷。

通俗來(lái)說(shuō)就是:根據(jù) pred 節(jié)點(diǎn)狀態(tài)來(lái)判斷當(dāng)前節(jié)點(diǎn)是否可以掛起,如果該方法返回 false,那么掛起條件還沒(méi)準(zhǔn)備好,就會(huì)重新進(jìn)入 acquireQueued(final Node node, int arg) 的自旋體,重新進(jìn)行判斷。如果返回 true,那就說(shuō)明當(dāng)前線程可以進(jìn)行掛起操作了,那么就會(huì)繼續(xù)執(zhí)行掛起。

這里需要注意的時(shí)候,節(jié)點(diǎn)的初始值為 0,因此如果獲取鎖失敗,會(huì)嘗試將節(jié)點(diǎn)設(shè)置為 SIGNAL。

繼續(xù)看掛起邏輯:

privatefinalboolean parkAndCheckInterrupt() {? LockSupport.park(this);returnThread.interrupted();}

LockSupport 是用來(lái)創(chuàng)建鎖和其他同步類的基本?線程阻塞?原語(yǔ)。LockSupport 提供 park() 和 unpark() 方法實(shí)現(xiàn)阻塞線程和解除線程阻塞。release 釋放鎖方法邏輯會(huì)調(diào)用 LockSupport.unPark 方法來(lái)喚醒后繼節(jié)點(diǎn)。

獲取獨(dú)占鎖流程圖:

釋放鎖

釋放鎖方法:

publicfinalbooleanrelease(intarg){if(tryRelease(arg)) {? ? Node h = head;if(h !=null&& h.waitStatus !=0)? ? ? unparkSuccessor(h);returntrue;? }returnfalse;}

釋放鎖的方法源碼就很好理解,通過(guò) tryRelease(arg) 方法嘗試釋放鎖,這個(gè)方法需要實(shí)現(xiàn)類自己實(shí)現(xiàn)釋放鎖的邏輯,釋放鎖成功后則執(zhí)行后面的喚醒后續(xù)節(jié)點(diǎn)的邏輯了,然后判斷 head 節(jié)點(diǎn)不為空并且 head 節(jié)點(diǎn)狀態(tài)不為 0,因?yàn)?addWaiter 方法默認(rèn)的節(jié)點(diǎn)狀態(tài)為 0,此時(shí)節(jié)點(diǎn)還沒(méi)有進(jìn)入就緒狀態(tài)。

繼續(xù)往下看源碼:

privatevoidunparkSuccessor(Node node){intws = node.waitStatus;if(ws <0)// 將頭節(jié)點(diǎn)的狀態(tài)設(shè)置為0// 這里會(huì)嘗試清除頭節(jié)點(diǎn)的狀態(tài),改為初始狀態(tài)compareAndSetWaitStatus(node, ws,0);// 后繼節(jié)點(diǎn)Node s = node.next;// 如果后繼節(jié)點(diǎn)為null,或者已經(jīng)被取消了if(s ==null|| s.waitStatus >0) {? ? s =null;// for循環(huán)從隊(duì)列尾部一直往前找可以喚醒的節(jié)點(diǎn)for(Node t = tail; t !=null&& t != node; t = t.prev)if(t.waitStatus <=0)? ? ? ? s = t;? }if(s !=null)// 喚醒后繼節(jié)點(diǎn)LockSupport.unpark(s.thread);}

從源碼可看出:?釋放鎖主要是將頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)喚醒,如果后繼節(jié)點(diǎn)不符合喚醒條件,則從隊(duì)尾一直往前找,直到找到符合條件的節(jié)點(diǎn)為止?。

總結(jié)

這篇文章主要講述了 AQS 的內(nèi)部結(jié)構(gòu)和它的同步實(shí)現(xiàn)原理,并從源碼的角度深度剖析了 AQS 獨(dú)占鎖模式下的獲取鎖與釋放鎖的邏輯,并且從源碼中我們得出:?在獨(dú)占鎖模式下,用 state 值表示鎖并且 0 表示無(wú)鎖狀態(tài),0 -> 1 表示從無(wú)鎖到有鎖,僅允許一條線程持有鎖,其余的線程會(huì)被包裝成一個(gè) Node 節(jié)點(diǎn)放到隊(duì)列中進(jìn)行掛起,隊(duì)列中的頭節(jié)點(diǎn)表示當(dāng)前正在執(zhí)行的線程,當(dāng)頭節(jié)點(diǎn)釋放后會(huì)喚醒后繼節(jié)點(diǎn),從而印證了 AQS 的隊(duì)列是一個(gè) FIFO 同步隊(duì)列。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372

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