高效并發(fā) - 13線程安全與鎖優(yōu)化

并發(fā)處理的廣泛應(yīng)用是使得Amdahl(阿姆達(dá))定律代替摩爾定律成為計(jì)算機(jī)性能發(fā)展源動(dòng)力的根本原因扳缕,也是人類“壓榨”計(jì)算機(jī)運(yùn)算能力的最有力武器慌闭。

1. 線程安全

《Java Concurrency In Practice》的作者Brian Goetz對(duì)“線程安全”有一個(gè)比較恰當(dāng)?shù)亩x:
當(dāng)多個(gè)線程訪問一個(gè)對(duì)象時(shí),如果不用考慮這些線程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交替執(zhí)行躯舔,也不需要進(jìn)行額外的同步驴剔,或者在調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作,調(diào)用這個(gè)對(duì)象的行為都可以獲得正確的結(jié)果粥庄,那這個(gè)對(duì)象是線程安全的.

定義要求線程安全的代碼都必須具備一個(gè)特征:代碼本身封裝了所有必要的正確性保障手段(如互斥同步等)丧失,令調(diào)用者無須關(guān)心多線程的問題,更無須自己采取任何措施來保證多線程的正確調(diào)用惜互。

1.1 Java語言中的線程安全

按照線程安全的“安全程度”由強(qiáng)至弱來排序布讹,可以將Java語言中各種操作共享的數(shù)據(jù)分為以下5類:不可變、絕對(duì)線程安全训堆、相對(duì)線程安全描验、線程兼容和線程對(duì)立。

1.不可變

不可變(Immutable)的對(duì)象一定是線程安全的.

只要一個(gè)不可變的對(duì)象被正確地構(gòu)建出來(沒有發(fā)生this引用逃逸的情況)坑鱼,那其外部的可見狀態(tài)永遠(yuǎn)也不會(huì)改變膘流,永遠(yuǎn)也不會(huì)看到它在多個(gè)線程之中處于不一致的狀態(tài)絮缅。“不可變”帶來的安全性是最簡單和最純粹的呼股。

Java API中符合不可變要求的類型:

  • String
  • 枚舉類型
  • Integer等基本數(shù)值包裝類
  • BigInteger, BigDecimal大數(shù)據(jù)類型
2.絕對(duì)線程安全

一個(gè)類要達(dá)到“不管運(yùn)行時(shí)環(huán)境如何耕魄,調(diào)用者都不需要任何額外的同步措施”的絕對(duì)安全通常需要付出很大的代價(jià)。在Java API中標(biāo)注自己是線程安全的類彭谁,大多數(shù)都不是絕對(duì)的線程安全吸奴。

3.相對(duì)線程安全

相對(duì)的線程安全就是我們通常意義上所講的線程安全,它需要保證對(duì)這個(gè)對(duì)象單獨(dú)的操作是線程安全的缠局,我們?cè)谡{(diào)用的時(shí)候不需要做額外的保障措施则奥,但是對(duì)于一些特定順序的連續(xù)調(diào)用,就可能需要在調(diào)用端使用額外的同步手段來保證調(diào)用的正確性甩鳄。

在Java語言中逞度,大部分的線程安全類都屬于這種類型,例如Vector妙啃、HashTable、Collections的synchronizedCollection()方法包裝的集合等俊戳。

4.線程兼容(單線程內(nèi)是安全的)

線程兼容是指對(duì)象本身并不是線程安全的揖赴,但是可以通過在調(diào)用端正確地使用同步手段來保證對(duì)象在并發(fā)環(huán)境中可以安全地使用,我們平常說一個(gè)類不是線程安全的抑胎,絕大多數(shù)時(shí)候指的是這一種情況燥滑。Java API中大部分的類都是屬于線程兼容的,如與前面的Vector和HashTable相對(duì)應(yīng)的集合類ArrayList和HashMap等阿逃。

5.線程對(duì)立

線程對(duì)立是指無論調(diào)用端是否采取了同步措施铭拧,都無法在多線程環(huán)境中并發(fā)使用的代碼。由于Java語言天生就具備多線程特性恃锉,線程對(duì)立這種排斥多線程的代碼是很少出現(xiàn)的搀菩,而且通常都是有害的,應(yīng)當(dāng)盡量避免破托。

一個(gè)線程對(duì)立的例子是Thread類的suspend()和resume()方法肪跋,如果有兩個(gè)線程同時(shí)持有一個(gè)線程對(duì)象,一個(gè)嘗試去中斷線程土砂,另一個(gè)嘗試去恢復(fù)線程州既,如果并發(fā)進(jìn)行的話,無論調(diào)用時(shí)是否進(jìn)行了同步萝映,目標(biāo)線程都是存在死鎖風(fēng)險(xiǎn)的吴叶,如果suspend()中斷的線程就是即將要執(zhí)行resume()的那個(gè)線程,那就肯定要產(chǎn)生死鎖了序臂。也正是由于這個(gè)原因蚌卤,suspend()和resume()方法已經(jīng)被JDK聲明廢棄(@Deprecated)了。常見的線程對(duì)立的操作還有System.setIn()、Sytem.setOut()和System.runFinalizersOnExit()等造寝。

1.2 線程安全的實(shí)現(xiàn)

1. 互斥同步(阻塞同步)
  • synchronized關(guān)鍵字

synchronized是原生語法層面的互斥鎖, 屬于一種悲觀的并發(fā)策略.

編譯之后磕洪,會(huì)在同步塊的前后分別形成monitorentermonitorexit這兩個(gè)字節(jié)碼指令,這兩個(gè)字節(jié)碼都需要一個(gè)reference類型的參數(shù)來指明要鎖定和解鎖的對(duì)象诫龙。如果Java程序中的synchronized明確指定了對(duì)象參數(shù)析显,那就是這個(gè)對(duì)象的reference;如果沒有明確指定签赃,那就根據(jù)synchronized修飾的是實(shí)例方法還是類方法谷异,去取對(duì)應(yīng)的對(duì)象實(shí)例或Class對(duì)象來作為鎖對(duì)象。

虛擬機(jī)在執(zhí)行monitorenter指令時(shí)锦聊,首先要嘗試獲取對(duì)象的鎖歹嘹。如果這個(gè)對(duì)象沒被鎖定囊嘉,或者當(dāng)前線程已經(jīng)擁有了那個(gè)對(duì)象的鎖猖任,把鎖的計(jì)數(shù)器加1,相應(yīng)的日丹,在執(zhí)行monitorexit指令時(shí)會(huì)將鎖計(jì)數(shù)器減1圆到,當(dāng)計(jì)數(shù)器為0時(shí)怎抛,鎖就被釋放。如果獲取對(duì)象鎖失敗芽淡,那當(dāng)前線程就要阻塞等待马绝,直到對(duì)象鎖被另外一個(gè)線程釋放為止。

在虛擬機(jī)規(guī)范對(duì)monitorenter和monitorexit的行為描述中挣菲,有兩點(diǎn)是需要特別注意的富稻。首先,synchronized同步塊對(duì)同一條線程來說是可重入的白胀,不會(huì)出現(xiàn)自己把自己鎖死的問題椭赋。其次,同步塊在已進(jìn)入的線程執(zhí)行完之前纹笼,會(huì)阻塞后面其他線程的進(jìn)入纹份。第12章講過,Java的線程是映射到操作系統(tǒng)的原生線程之上的廷痘,如果要阻塞或喚醒一個(gè)線程蔓涧,都需要操作系統(tǒng)來幫忙完成,這就需要從用戶態(tài)轉(zhuǎn)換到核心態(tài)中笋额,因此狀態(tài)轉(zhuǎn)換需要耗費(fèi)很多的處理器時(shí)間元暴。對(duì)于代碼簡單的同步塊(如被synchronized修飾的getter()或setter()方法),狀態(tài)轉(zhuǎn)換消耗的時(shí)間有可能比用戶代碼執(zhí)行的時(shí)間還要長兄猩。所以synchronized是Java語言中一個(gè)重量級(jí)(Heavyweight)的操作.

  • java.util.concurrent(下文稱J.U.C)包中的重入鎖(ReentrantLock)

ReentrantLock 通過API層面的互斥鎖(lock()和unlock()方法配合try/finally語句塊來完成)

ReentrantLock增加了一些高級(jí)功能茉盏,主要有以下3項(xiàng):等待可中斷鉴未、可實(shí)現(xiàn)公平鎖,以及鎖可以綁定多個(gè)條件鸠姨。

  • 等待可中斷是指當(dāng)持有鎖的線程長期不釋放鎖的時(shí)候铜秆,正在等待的線程可以選擇放棄等待,改為處理其他事情讶迁,
  • 公平鎖是指多個(gè)線程在等待同一個(gè)鎖時(shí)连茧,必須按照申請(qǐng)鎖的時(shí)間順序來依次獲得鎖;而非公平鎖則不保證這一點(diǎn)巍糯,在鎖被釋放時(shí)啸驯,任何一個(gè)等待鎖的線程都有機(jī)會(huì)獲得鎖。synchronized中的鎖是非公平的祟峦,ReentrantLock默認(rèn)情況下也是非公平的罚斗。
  • 鎖綁定多個(gè)條件是指一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定多個(gè)Condition對(duì)象,而在synchronized中宅楞,鎖對(duì)象的wait()和notify()或notifyAll()方法可以實(shí)現(xiàn)一個(gè)隱含的條件针姿,如果要和多于一個(gè)的條件關(guān)聯(lián)的時(shí)候,就不得不額外地添加一個(gè)鎖厌衙,而ReentrantLock則無須這樣做搓幌,只需要多次調(diào)用newCondition()方法即可。

synchronized在JDK1.5之前多線程環(huán)境下吞吐比ReentrantLock差很多, 在JDK1.6的優(yōu)化后, 兩者基本持平. 但虛擬機(jī)在未來的性能改進(jìn)中肯定也會(huì)更加偏向于原生的synchronized迅箩,所以提倡優(yōu)先考慮synchronized

2.非阻塞同步

是基于沖突檢測的樂觀并發(fā)策略, 即先進(jìn)行操作处铛,如果沒有其他線程爭用共享數(shù)據(jù)饲趋,那操作就成功了;如果共享數(shù)據(jù)有爭用撤蟆,產(chǎn)生了沖突奕塑,那就再采取其他的補(bǔ)償措施(最常見的補(bǔ)償措施就是不斷地重試,直到成功為止)家肯,這種樂觀的并發(fā)策略的許多實(shí)現(xiàn)都不需要把線程掛起.

因?yàn)樾枰僮骱蜎_突檢測這兩個(gè)步驟具備原子性, 因此使用樂觀并發(fā)策略需要“硬件指令集"支持. 類似的指令有如下, (后面兩條是現(xiàn)代處理器新增的指令).

  • 測試并設(shè)置(Test-and-Set)龄砰。
  • 獲取并增加(Fetch-and-Increment)。
  • 交換(Swap)讨衣。
  • 比較并交換(Compare-and-Swap换棚,下文稱CAS)。
  • 加載鏈接/條件存儲(chǔ)(Load-Linked/Store-Conditional反镇,下文稱LL/SC)固蚤。

CAS指令需要有3個(gè)操作數(shù),分別是內(nèi)存位置(在Java中可以簡單理解為變量的內(nèi)存地址歹茶,用V表示)夕玩、舊的預(yù)期值(用A表示)和新值(用B表示)你弦。CAS指令執(zhí)行時(shí),當(dāng)且僅當(dāng)V符合舊預(yù)期值A(chǔ)時(shí)燎孟,處理器用新值B更新V的值禽作,否則它就不執(zhí)行更新,但是無論是否更新了V的值揩页,都會(huì)返回V的舊值旷偿,上述的處理過程是一個(gè)原子操作。

在JDK 1.5之后碍沐,Java程序中才可以使用CAS操作狸捅,該操作由sun.misc.Unsafe類里面的compareAndSwapInt()和compareAndSwapLong()等幾個(gè)方法包裝提供.
Unsafe類不是提供給用戶程序調(diào)用的類(Unsafe.getUnsafe()@CallerSensitive注解了, 限制了只有啟動(dòng)類加載器(Bootstrap ClassLoader)加載的Class才能訪問它).

  • ABA問題

CAS操作存在這樣的一個(gè)邏輯漏洞:如果一個(gè)變量V初次讀取的時(shí)候是A值,并且在準(zhǔn)備賦值的時(shí)候檢查到它仍然為A值累提,那我們就能說它的值沒有被其他線程改變過了嗎尘喝?如果在這段期間它的值曾經(jīng)被改成了B,后來又被改回為A斋陪,那CAS操作就會(huì)誤認(rèn)為它從來沒有被改變過朽褪。這個(gè)漏洞稱為CAS操作的"ABA"問題。

解決方案:

  1. J.U.C包為了解決這個(gè)問題无虚,提供了一個(gè)帶有標(biāo)記的原子引用類AtomicStampedReference缔赠,它可以通過控制變量值的版本來保證CAS的正確性。不過目前來說這個(gè)類比較“雞肋”友题,大部分情況下ABA問題不會(huì)影響程序并發(fā)的正確性嗤堰,
  2. 如果需要解決ABA問題,改用傳統(tǒng)的互斥同步可能會(huì)比原子類更高效度宦。
3.無同步方案

如果一個(gè)方法本來就不涉及共享數(shù)據(jù)踢匣,那它自然就無須任何同步措施去保證正確性,因此會(huì)有一些代碼天生就是線程安全的.

  • 可重入代碼(Reentrant Code)

這種代碼也叫做純代碼(Pure Code)戈抄,可以在代碼執(zhí)行的任何時(shí)刻中斷它离唬,轉(zhuǎn)而去執(zhí)行另外一段代碼(包括遞歸調(diào)用它本身),而在控制權(quán)返回后划鸽,原來的程序不會(huì)出現(xiàn)任何錯(cuò)誤输莺。

判斷代碼是否具備可重入性:如果一個(gè)方法,它的返回結(jié)果是可以預(yù)測的裸诽,只要輸入了相同的數(shù)據(jù)嫂用,就都能返回相同的結(jié)果,那它就滿足可重入性的要求崭捍,當(dāng)然也就是線程安全的尸折。

  • 線程本地存儲(chǔ)(Thread Local Storage)

如果一段代碼中所需要的數(shù)據(jù)必須與其他代碼共享,那就看看這些共享數(shù)據(jù)的代碼是否能保證在同一個(gè)線程中執(zhí)行殷蛇?如果能保證实夹,我們就可以把共享數(shù)據(jù)的可見范圍限制在同一個(gè)線程之內(nèi)


2. 鎖優(yōu)化

高效并發(fā)是從JDK 1.5到JDK 1.6的一個(gè)重要改進(jìn)橄浓,HotSpot虛擬機(jī)開發(fā)團(tuán)隊(duì)在這個(gè)版本上花費(fèi)了大量的精力去實(shí)現(xiàn)各種鎖優(yōu)化技術(shù),如適應(yīng)性自旋(Adaptive Spinning)亮航、鎖消除(Lock Elimination)荸实、鎖粗化(Lock Coarsening)、輕量級(jí)鎖(Lightweight Locking)和偏向鎖(Biased Locking)等.

2.1 自旋鎖與自適應(yīng)自旋

互斥同步對(duì)性能最大的影響是阻塞的實(shí)現(xiàn)缴淋,掛起線程和恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成准给,這些操作給系統(tǒng)的并發(fā)性能帶來了很大的壓力. 為了短暫的鎖定時(shí)間而去去掛起和恢復(fù)線程并不值得。在不放棄處理器的執(zhí)行時(shí)間重抖,看看持有鎖的線程是否很快就會(huì)釋放鎖露氮。為了讓線程等待,我們只需讓線程執(zhí)行一個(gè)忙循環(huán)(自旋)钟沛,這項(xiàng)技術(shù)就是所謂的自旋鎖畔规。

自旋鎖在JDK 1.6之后默認(rèn)開啟, 如果自旋超過了限定的次數(shù)仍然沒有成功獲得鎖,就應(yīng)當(dāng)使用傳統(tǒng)的方式去掛起線程了恨统。自旋次數(shù)的默認(rèn)值是10次叁扫,用戶可以使用參數(shù)-XX:PreBlockSpin來更改。

在JDK 1.6中引入了自適應(yīng)的自旋鎖畜埋。自適應(yīng)意味著自旋的時(shí)間不再固定了莫绣,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來決定。如果在同一個(gè)鎖對(duì)象上悠鞍,自旋等待剛剛成功獲得過鎖对室,并且持有鎖的線程正在運(yùn)行中,那么虛擬機(jī)就會(huì)認(rèn)為這次自旋也很有可能再次成功咖祭,進(jìn)而它將允許自旋等待持續(xù)相對(duì)更長的時(shí)間软驰,比如100個(gè)循環(huán)。另外心肪,如果對(duì)于某個(gè)鎖,自旋很少成功獲得過纠吴,那在以后要獲取這個(gè)鎖時(shí)將可能省略掉自旋過程硬鞍,以避免浪費(fèi)處理器資源。有了自適應(yīng)自旋戴已,隨著程序運(yùn)行和性能監(jiān)控信息的不斷完善固该,虛擬機(jī)對(duì)程序鎖的狀況預(yù)測就會(huì)越來越準(zhǔn)確,虛擬機(jī)就會(huì)變得越來越“聰明”了糖儡。

2.2 鎖消除(Lock Elimination)

鎖消除是指虛擬機(jī)即時(shí)編譯器在運(yùn)行時(shí)伐坏,對(duì)一些代碼上要求同步,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進(jìn)行消除握联。鎖消除的主要判定依據(jù)來源于逃逸分析的數(shù)據(jù)支持(第11章已經(jīng)講解過逃逸分析技術(shù))桦沉,如果判斷在一段代碼中每瞒,堆上的所有數(shù)據(jù)都不會(huì)逃逸出去從而被其他線程訪問到,那就可以把它們當(dāng)做棧上數(shù)據(jù)對(duì)待纯露,認(rèn)為它們是線程私有的剿骨,同步加鎖自然就無須進(jìn)行

2.3 鎖粗化(Lock Coarsening)

原則上埠褪,我們?cè)诰帉懘a的時(shí)候浓利,總是推薦將同步塊的作用范圍限制得盡量小——只在共享數(shù)據(jù)的實(shí)際作用域中才進(jìn)行同步,這樣是為了使得需要同步的操作數(shù)量盡可能變小钞速,如果存在鎖競爭贷掖,那等待鎖的線程也能盡快拿到鎖。

但是如果一系列的連續(xù)操作都對(duì)同一個(gè)對(duì)象反復(fù)加鎖和解鎖渴语,甚至加鎖操作是出現(xiàn)在循環(huán)體中的苹威,那即使沒有線程競爭,頻繁地進(jìn)行互斥同步操作也會(huì)導(dǎo)致不必要的性能損耗遵班。虛擬機(jī)探測到有這樣一串零碎的操作都對(duì)同一個(gè)對(duì)象加鎖屠升,將會(huì)把加鎖同步的范圍擴(kuò)展(粗化)到整個(gè)操作序列的外部.

2.4 輕量級(jí)鎖(Lightweight Locking)和偏向鎖(Biased Locking)

“阻塞或喚醒一個(gè)Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成,這種狀態(tài)轉(zhuǎn)換需要耗費(fèi)處理器時(shí)間狭郑。如果同步代碼塊中的內(nèi)容過于簡單腹暖,狀態(tài)轉(zhuǎn)換消耗的時(shí)間有可能比用戶代碼執(zhí)行的時(shí)間還要長”。這種方式就是synchronized最初實(shí)現(xiàn)互斥同步鎖的方式翰萨,這就是JDK 6之前synchronized效率低的原因, 這種依賴于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱之為“重量級(jí)鎖”脏答,JDK 1.6中為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”亩鬼。

輕量級(jí)鎖是在無競爭的情況下使用CAS操作去減少傳統(tǒng)的重量級(jí)鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗殖告。輕量級(jí)鎖所適應(yīng)的場景是多個(gè)線程交替無競爭執(zhí)行同步塊的情況,如果存在同一時(shí)間訪問同一鎖的情況雳锋,就會(huì)導(dǎo)致輕量級(jí)鎖膨脹為重量級(jí)鎖黄绩。`

偏向鎖是在單線程無競爭的情況下把輕量級(jí)鎖的獲取及釋放需要進(jìn)行的CAS操作都消除掉,只進(jìn)行置換ThreadID操作的時(shí)候依賴一次CAS操作**.

輕量級(jí)鎖是為了在線程交替執(zhí)行同步塊時(shí)提高性能玷过,而偏向鎖則是在只有一個(gè)線程執(zhí)行同步塊時(shí)進(jìn)一步提高性能爽丹。

  • 應(yīng)用場景
    偏向鎖:只有一個(gè)線程進(jìn)入臨界區(qū);
    輕量級(jí)鎖:多個(gè)線程交替進(jìn)入臨界區(qū)辛蚊;
    重量級(jí)鎖:多個(gè)線程同時(shí)進(jìn)入臨界區(qū)粤蝎。

鎖的狀態(tài)變更: 無鎖 --> 偏向鎖 --> 輕量級(jí)鎖 --> 重量級(jí)鎖。鎖狀態(tài)只能升級(jí)不能降級(jí)袋马。

輕量級(jí)鎖跟偏向鎖是借助對(duì)象實(shí)例的對(duì)象頭中的Mark Word來實(shí)現(xiàn)的.

Mark Work數(shù)據(jù)說明

  • 鎖狀態(tài)轉(zhuǎn)變圖
synchronized原理-內(nèi)部鎖類型裝換過程
  • 輕量級(jí)鎖的CAS操作圖


    輕量級(jí)鎖CAS操作之前堆棧與對(duì)象頭的狀態(tài)

    輕量級(jí)鎖CAS操作之后堆棧與對(duì)象頭的狀態(tài)

補(bǔ)充:

  • 偏向鎖的撤銷
    偏向鎖持有Thread不會(huì)主動(dòng)釋放偏向鎖, 只有遇到競爭Thread嘗試競爭偏向鎖時(shí)初澎,發(fā)現(xiàn)Mark Word中的Thread Id并不是自己, 則向JVM提交一個(gè)時(shí)間停止的請(qǐng)求: 在safe point的時(shí)候,暫定持有者Thread, JVM線程偽造一個(gè)displaced mark word到持有鎖Thread的棧上虑凛,object的mark work更新為輕量鎖模式碑宴。然后再喚醒鎖的持有者Thread软啼。這樣持有者Thread就會(huì)執(zhí)行輕量鎖的邏輯, 而競爭者Thread也會(huì)按照輕量鎖的模式去競爭鎖。

  • 偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的墓懂⊙嫘可以通過JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)捕仔。

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末榜跌,一起剝皮案震驚了整個(gè)濱河市闪唆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钓葫,老刑警劉巖悄蕾,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異础浮,居然都是意外死亡帆调,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門豆同,熙熙樓的掌柜王于貴愁眉苦臉地迎上來番刊,“玉大人,你說我怎么就攤上這事影锈∏畚瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵鸭廷,是天一觀的道長枣抱。 經(jīng)常有香客問我,道長辆床,這世上最難降的妖魔是什么佳晶? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮讼载,結(jié)果婚禮上宵晚,老公的妹妹穿的比我還像新娘。我一直安慰自己维雇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布晒他。 她就那樣靜靜地躺著吱型,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陨仅。 梳的紋絲不亂的頭發(fā)上津滞,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天铝侵,我揣著相機(jī)與錄音,去河邊找鬼触徐。 笑死咪鲜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撞鹉。 我是一名探鬼主播疟丙,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸟雏!你這毒婦竟也來了享郊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤孝鹊,失蹤者是張志新(化名)和其女友劉穎炊琉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體又活,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苔咪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柳骄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片团赏。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖夹界,靈堂內(nèi)的尸體忽然破棺而出馆里,到底是詐尸還是另有隱情,我是刑警寧澤可柿,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布鸠踪,位于F島的核電站,受9級(jí)特大地震影響复斥,放射性物質(zhì)發(fā)生泄漏营密。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一目锭、第九天 我趴在偏房一處隱蔽的房頂上張望评汰。 院中可真熱鬧,春花似錦痢虹、人聲如沸被去。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惨缆。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坯墨,已是汗流浹背寂汇。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捣染,地道東北人骄瓣。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像耍攘,于是被迫代替她去往敵國和親榕栏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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