Java 理論與實(shí)踐: 正確使用 Volatile 變量--轉(zhuǎn)

原文地址:http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

Java 語言中的 volatile 變量可以被看作是一種 “程度較輕的synchronized”铭乾;與synchronized塊相比,volatile 變量所需的編碼較少,并且運(yùn)行時(shí)開銷也較少回梧,但是它所能實(shí)現(xiàn)的功能也僅是synchronized的一部分焕窝。本文介紹了幾種有效使用 volatile 變量的模式唧取,并強(qiáng)調(diào)了幾種不適合使用 volatile 變量的情形肄梨。

鎖提供了兩種主要特性:互斥(mutual exclusion)可見性(visibility)莺匠。互斥即一次只允許一個(gè)線程持有某個(gè)特定的鎖懒构,因此可使用該特性實(shí)現(xiàn)對(duì)共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議餐济,這樣,一次就只有一個(gè)線程能夠使用該共享數(shù)據(jù)胆剧⌒跄罚可見性要更加復(fù)雜一些,它必須確保釋放鎖之前對(duì)共享數(shù)據(jù)做出的更改對(duì)于隨后獲得該鎖的另一個(gè)線程是可見的 —— 如果沒有同步機(jī)制提供的這種可見性保證秩霍,線程看到的共享變量可能是修改前的值或不一致的值篙悯,這將引發(fā)許多嚴(yán)重問題。

Volatile 變量

Volatile 變量具有synchronized的可見性特性前域,但是不具備原子特性辕近。這就是說線程能夠自動(dòng)發(fā)現(xiàn) volatile 變量的最新值。Volatile 變量可用于提供線程安全匿垄,但是只能應(yīng)用于非常有限的一組用例:多個(gè)變量之間或者某個(gè)變量的當(dāng)前值與修改后值之間沒有約束移宅。因此,單獨(dú)使用 volatile 還不足以實(shí)現(xiàn)計(jì)數(shù)器椿疗、互斥鎖或任何具有與多個(gè)變量相關(guān)的不變式(Invariants)的類(例如 “start <=end”)漏峰。

出于簡(jiǎn)易性或可伸縮性的考慮,您可能傾向于使用 volatile 變量而不是鎖届榄。當(dāng)使用 volatile 變量而非鎖時(shí)浅乔,某些習(xí)慣用法(idiom)更加易于編碼和閱讀。此外铝条,volatile 變量不會(huì)像鎖那樣造成線程阻塞靖苇,因此也很少造成可伸縮性問題。在某些情況下班缰,如果讀操作遠(yuǎn)遠(yuǎn)大于寫操作贤壁,volatile 變量還可以提供優(yōu)于鎖的性能優(yōu)勢(shì)。

正確使用 volatile 變量的條件

您只能在有限的一些情形下使用 volatile 變量替代鎖埠忘。要使 volatile 變量提供理想的線程安全脾拆,必須同時(shí)滿足下面兩個(gè)條件:

對(duì)變量的寫操作不依賴于當(dāng)前值。

該變量沒有包含在具有其他變量的不變式中莹妒。

實(shí)際上名船,這些條件表明,可以被寫入 volatile 變量的這些有效值獨(dú)立于任何程序的狀態(tài)旨怠,包括變量的當(dāng)前狀態(tài)渠驼。

第一個(gè)條件的限制使 volatile 變量不能用作線程安全計(jì)數(shù)器。雖然增量操作(x++)看上去類似一個(gè)單獨(dú)操作运吓,實(shí)際上它是一個(gè)由讀瓤拾睢-修改-寫入操作序列組成的組合操作疯趟,必須以原子方式執(zhí)行,而 volatile 不能提供必須的原子特性谋梭。實(shí)現(xiàn)正確的操作需要使x的值在操作期間保持不變信峻,而 volatile 變量無法實(shí)現(xiàn)這點(diǎn)。(然而瓮床,如果將值調(diào)整為只從單個(gè)線程寫入盹舞,那么可以忽略第一個(gè)條件。)

大多數(shù)編程情形都會(huì)與這兩個(gè)條件的其中之一沖突隘庄,使得 volatile 變量不能像synchronized那樣普遍適用于實(shí)現(xiàn)線程安全踢步。清單 1 顯示了一個(gè)非線程安全的數(shù)值范圍類。它包含了一個(gè)不變式 —— 下界總是小于或等于上界丑掺。

清單 1. 非線程安全的數(shù)值范圍類

@NotThreadSafe

public class NumberRange {

? ? private int lower, upper;

? ? public int getLower() { return lower; }

? ? public int getUpper() { return upper; }

? ? public void setLower(int value) {

? ? ? ? if (value > upper)

? ? ? ? ? ? throw new IllegalArgumentException(...);

? ? ? ? lower = value;

? ? }

? ? public void setUpper(int value) {

? ? ? ? if (value < lower)

? ? ? ? ? ? throw new IllegalArgumentException(...);

? ? ? ? upper = value;

? ? }

}

這種方式限制了范圍的狀態(tài)變量获印,因此將lower和 upper 字段定義為 volatile 類型不能夠充分實(shí)現(xiàn)類的線程安全;從而仍然需要使用同步街州。否則兼丰,如果湊巧兩個(gè)線程在同一時(shí)間使用不一致的值執(zhí)行setLower和setUpper的話,則會(huì)使范圍處于不一致的狀態(tài)唆缴。例如鳍征,如果初始狀態(tài)是(0, 5),同一時(shí)間內(nèi)面徽,線程 A 調(diào)用setLower(4)并且線程 B 調(diào)用setUpper(3)艳丛,顯然這兩個(gè)操作交叉存入的值是不符合條件的,那么兩個(gè)線程都會(huì)通過用于保護(hù)不變式的檢查趟紊,使得最后的范圍值是(4, 3)—— 一個(gè)無效值氮双。至于針對(duì)范圍的其他操作,我們需要使setLower()和setUpper()操作原子化 —— 而將字段定義為 volatile 類型是無法實(shí)現(xiàn)這一目的的霎匈。

性能考慮

使用 volatile 變量的主要原因是其簡(jiǎn)易性:在某些情形下眶蕉,使用 volatile 變量要比使用相應(yīng)的鎖簡(jiǎn)單得多。使用 volatile 變量次要原因是其性能:某些情況下唧躲,volatile 變量同步機(jī)制的性能要優(yōu)于鎖。

很難做出準(zhǔn)確碱璃、全面的評(píng)價(jià)弄痹,例如 “X 總是比 Y 快”,尤其是對(duì) JVM 內(nèi)在的操作而言嵌器。(例如肛真,某些情況下 VM 也許能夠完全刪除鎖機(jī)制,這使得我們難以抽象地比較volatile和synchronized的開銷爽航。)就是說蚓让,在目前大多數(shù)的處理器架構(gòu)上乾忱,volatile 讀操作開銷非常低 —— 幾乎和非 volatile 讀操作一樣。而 volatile 寫操作的開銷要比非 volatile 寫操作多很多历极,因?yàn)橐WC可見性需要實(shí)現(xiàn)內(nèi)存界定(Memory Fence)窄瘟,即便如此,volatile 的總開銷仍然要比鎖獲取低趟卸。

volatile 操作不會(huì)像鎖一樣造成阻塞蹄葱,因此,在能夠安全使用 volatile 的情況下锄列,volatile 可以提供一些優(yōu)于鎖的可伸縮特性图云。如果讀操作的次數(shù)要遠(yuǎn)遠(yuǎn)超過寫操作,與鎖相比邻邮,volatile 變量通常能夠減少同步的性能開銷竣况。

正確使用 volatile 的模式

很多并發(fā)性專家事實(shí)上往往引導(dǎo)用戶遠(yuǎn)離 volatile 變量,因?yàn)槭褂盟鼈円仁褂面i更加容易出錯(cuò)筒严。然而丹泉,如果謹(jǐn)慎地遵循一些良好定義的模式,就能夠在很多場(chǎng)合內(nèi)安全地使用 volatile 變量萝风。要始終牢記使用 volatile 的限制 —— 只有在狀態(tài)真正獨(dú)立于程序內(nèi)其他內(nèi)容時(shí)才能使用 volatile —— 這條規(guī)則能夠避免將這些模式擴(kuò)展到不安全的用例嘀掸。

模式 #1:狀態(tài)標(biāo)志

也許實(shí)現(xiàn) volatile 變量的規(guī)范使用僅僅是使用一個(gè)布爾狀態(tài)標(biāo)志,用于指示發(fā)生了一個(gè)重要的一次性事件规惰,例如完成初始化或請(qǐng)求停機(jī)睬塌。

很多應(yīng)用程序包含了一種控制結(jié)構(gòu),形式為 “在還沒有準(zhǔn)備好停止程序時(shí)再執(zhí)行一些工作”歇万,如清單 2 所示:

清單 2. 將 volatile 變量作為狀態(tài)標(biāo)志使用

volatile boolean shutdownRequested;

...

public void shutdown() { shutdownRequested = true; }

public void doWork() {

? ? while (!shutdownRequested) {

? ? ? ? // do stuff

? ? }

}

很可能會(huì)從循環(huán)外部調(diào)用shutdown()方法 —— 即在另一個(gè)線程中 —— 因此揩晴,需要執(zhí)行某種同步來確保正確實(shí)現(xiàn)shutdownRequested變量的可見性。(可能會(huì)從 JMX 偵聽程序贪磺、GUI 事件線程中的操作偵聽程序硫兰、通過 RMI 、通過一個(gè) Web 服務(wù)等調(diào)用)寒锚。然而劫映,使用synchronized塊編寫循環(huán)要比使用清單 2 所示的 volatile 狀態(tài)標(biāo)志編寫麻煩很多。由于 volatile 簡(jiǎn)化了編碼刹前,并且狀態(tài)標(biāo)志并不依賴于程序內(nèi)任何其他狀態(tài)泳赋,因此此處非常適合使用 volatile。

這種類型的狀態(tài)標(biāo)記的一個(gè)公共特性是:通常只有一種狀態(tài)轉(zhuǎn)換喇喉;shutdownRequested標(biāo)志從false轉(zhuǎn)換為true祖今,然后程序停止。這種模式可以擴(kuò)展到來回轉(zhuǎn)換的狀態(tài)標(biāo)志,但是只有在轉(zhuǎn)換周期不被察覺的情況下才能擴(kuò)展(從false到true千诬,再轉(zhuǎn)換到false)耍目。此外,還需要某些原子狀態(tài)轉(zhuǎn)換機(jī)制徐绑,例如原子變量邪驮。

模式 #2:一次性安全發(fā)布(one-time safe publication)

缺乏同步會(huì)導(dǎo)致無法實(shí)現(xiàn)可見性,這使得確定何時(shí)寫入對(duì)象引用而不是原語值變得更加困難泵三。在缺乏同步的情況下耕捞,可能會(huì)遇到某個(gè)對(duì)象引用的更新值(由另一個(gè)線程寫入)和該對(duì)象狀態(tài)的舊值同時(shí)存在。(這就是造成著名的雙重檢查鎖定(double-checked-locking)問題的根源烫幕,其中對(duì)象引用在沒有同步的情況下進(jìn)行讀操作俺抽,產(chǎn)生的問題是您可能會(huì)看到一個(gè)更新的引用,但是仍然會(huì)通過該引用看到不完全構(gòu)造的對(duì)象)较曼。

實(shí)現(xiàn)安全發(fā)布對(duì)象的一種技術(shù)就是將對(duì)象引用定義為 volatile 類型磷斧。清單 3 展示了一個(gè)示例,其中后臺(tái)線程在啟動(dòng)階段從數(shù)據(jù)庫(kù)加載一些數(shù)據(jù)捷犹。其他代碼在能夠利用這些數(shù)據(jù)時(shí)弛饭,在使用之前將檢查這些數(shù)據(jù)是否曾經(jīng)發(fā)布過。

清單 3. 將 volatile 變量用于一次性安全發(fā)布

public class BackgroundFloobleLoader {

? ? public volatile Flooble theFlooble;

? ? public void initInBackground() {

? ? ? ? // do lots of stuff

? ? ? ? theFlooble = new Flooble();? // this is the only write to theFlooble

? ? }

}

public class SomeOtherClass {

? ? public void doWork() {

? ? ? ? while (true) {

? ? ? ? ? ? // do some stuff...

? ? ? ? ? ? // use the Flooble, but only if it is ready

? ? ? ? ? ? if (floobleLoader.theFlooble != null)

? ? ? ? ? ? ? ? doSomething(floobleLoader.theFlooble);

? ? ? ? }

? ? }

}

如果theFlooble引用不是 volatile 類型萍歉,doWork()中的代碼在解除對(duì)theFlooble的引用時(shí)侣颂,將會(huì)得到一個(gè)不完全構(gòu)造的Flooble。

該模式的一個(gè)必要條件是:被發(fā)布的對(duì)象必須是線程安全的枪孩,或者是有效的不可變對(duì)象(有效不可變意味著對(duì)象的狀態(tài)在發(fā)布之后永遠(yuǎn)不會(huì)被修改)憔晒。volatile 類型的引用可以確保對(duì)象的發(fā)布形式的可見性,但是如果對(duì)象的狀態(tài)在發(fā)布后將發(fā)生更改蔑舞,那么就需要額外的同步拒担。

模式 #3:獨(dú)立觀察(independent observation)

安全使用 volatile 的另一種簡(jiǎn)單模式是:定期 “發(fā)布” 觀察結(jié)果供程序內(nèi)部使用。例如攻询,假設(shè)有一種環(huán)境傳感器能夠感覺環(huán)境溫度从撼。一個(gè)后臺(tái)線程可能會(huì)每隔幾秒讀取一次該傳感器,并更新包含當(dāng)前文檔的 volatile 變量钧栖。然后低零,其他線程可以讀取這個(gè)變量,從而隨時(shí)能夠看到最新的溫度值拯杠。

使用該模式的另一種應(yīng)用程序就是收集程序的統(tǒng)計(jì)信息毁兆。清單 4 展示了身份驗(yàn)證機(jī)制如何記憶最近一次登錄的用戶的名字。將反復(fù)使用lastUser引用來發(fā)布值阴挣,以供程序的其他部分使用。

清單 4. 將 volatile 變量用于多個(gè)獨(dú)立觀察結(jié)果的發(fā)布

public class UserManager {

? ? public volatile String lastUser;

? ? public boolean authenticate(String user, String password) {

? ? ? ? boolean valid = passwordIsValid(user, password);

? ? ? ? if (valid) {

? ? ? ? ? ? User u = new User();

? ? ? ? ? ? activeUsers.add(u);

? ? ? ? ? ? lastUser = user;

? ? ? ? }

? ? ? ? return valid;

? ? }

}

該模式是前面模式的擴(kuò)展纺腊;將某個(gè)值發(fā)布以在程序內(nèi)的其他地方使用畔咧,但是與一次性事件的發(fā)布不同茎芭,這是一系列獨(dú)立事件。這個(gè)模式要求被發(fā)布的值是有效不可變的 —— 即值的狀態(tài)在發(fā)布后不會(huì)更改誓沸。使用該值的代碼需要清楚該值可能隨時(shí)發(fā)生變化梅桩。

模式 #4:“volatile bean” 模式

volatile bean 模式適用于將 JavaBeans 作為“榮譽(yù)結(jié)構(gòu)”使用的框架。在 volatile bean 模式中拜隧,JavaBean 被用作一組具有 getter 和/或 setter 方法 的獨(dú)立屬性的容器宿百。volatile bean 模式的基本原理是:很多框架為易變數(shù)據(jù)的持有者(例如HttpSession)提供了容器,但是放入這些容器中的對(duì)象必須是線程安全的洪添。

在 volatile bean 模式中垦页,JavaBean 的所有數(shù)據(jù)成員都是 volatile 類型的,并且 getter 和 setter 方法必須非常普通 —— 除了獲取或設(shè)置相應(yīng)的屬性外干奢,不能包含任何邏輯痊焊。此外,對(duì)于對(duì)象引用的數(shù)據(jù)成員忿峻,引用的對(duì)象必須是有效不可變的薄啥。(這將禁止具有數(shù)組值的屬性,因?yàn)楫?dāng)數(shù)組引用被聲明為volatile時(shí)逛尚,只有引用而不是數(shù)組本身具有 volatile 語義)垄惧。對(duì)于任何 volatile 變量,不變式或約束都不能包含 JavaBean 屬性绰寞。清單 5 中的示例展示了遵守 volatile bean 模式的 JavaBean:

清單 5. 遵守 volatile bean 模式的 Person 對(duì)象

@ThreadSafe

public class Person {

? ? private volatile String firstName;

? ? private volatile String lastName;

? ? private volatile int age;

? ? public String getFirstName() { return firstName; }

? ? public String getLastName() { return lastName; }

? ? public int getAge() { return age; }

? ? public void setFirstName(String firstName) {

? ? ? ? this.firstName = firstName;

? ? }

? ? public void setLastName(String lastName) {

? ? ? ? this.lastName = lastName;

? ? }

? ? public void setAge(int age) {

? ? ? ? this.age = age;

? ? }

}

volatile 的高級(jí)模式

前面幾節(jié)介紹的模式涵蓋了大部分的基本用例到逊,在這些模式中使用 volatile 非常有用并且簡(jiǎn)單。這一節(jié)將介紹一種更加高級(jí)的模式克握,在該模式中蕾管,volatile 將提供性能或可伸縮性優(yōu)勢(shì)。

volatile 應(yīng)用的的高級(jí)模式非常脆弱菩暗。因此掰曾,必須對(duì)假設(shè)的條件仔細(xì)證明,并且這些模式被嚴(yán)格地封裝了起來停团,因?yàn)榧词狗浅P〉母囊矔?huì)損壞您的代碼旷坦!同樣,使用更高級(jí)的 volatile 用例的原因是它能夠提升性能佑稠,確保在開始應(yīng)用高級(jí)模式之前秒梅,真正確定需要實(shí)現(xiàn)這種性能獲益。需要對(duì)這些模式進(jìn)行權(quán)衡舌胶,放棄可讀性或可維護(hù)性來換取可能的性能收益 —— 如果您不需要提升性能(或者不能夠通過一個(gè)嚴(yán)格的測(cè)試程序證明您需要它)捆蜀,那么這很可能是一次糟糕的交易,因?yàn)槟芸赡軙?huì)得不償失,換來的東西要比放棄的東西價(jià)值更低辆它。

模式 #5:開銷較低的讀-寫鎖策略

目前為止誊薄,您應(yīng)該了解了 volatile 的功能還不足以實(shí)現(xiàn)計(jì)數(shù)器。因?yàn)?+x實(shí)際上是三種操作(讀锰茉、添加呢蔫、存儲(chǔ))的簡(jiǎn)單組合,如果多個(gè)線程湊巧試圖同時(shí)對(duì) volatile 計(jì)數(shù)器執(zhí)行增量操作飒筑,那么它的更新值有可能會(huì)丟失片吊。

然而,如果讀操作遠(yuǎn)遠(yuǎn)超過寫操作协屡,您可以結(jié)合使用內(nèi)部鎖和 volatile 變量來減少公共代碼路徑的開銷俏脊。清單 6 中顯示的線程安全的計(jì)數(shù)器使用synchronized確保增量操作是原子的,并使用volatile保證當(dāng)前結(jié)果的可見性著瓶。如果更新不頻繁的話联予,該方法可實(shí)現(xiàn)更好的性能,因?yàn)樽x路徑的開銷僅僅涉及 volatile 讀操作材原,這通常要優(yōu)于一個(gè)無競(jìng)爭(zhēng)的鎖獲取的開銷沸久。

清單 6. 結(jié)合使用 volatile 和 synchronized 實(shí)現(xiàn) “開銷較低的讀-寫鎖”

@ThreadSafepublic class CheesyCounter {? ? // Employs the cheap read-write lock trick? ? // All mutative operations MUST be done with the 'this' lock held? ? @GuardedBy("this") privatevolatileint value;? ? public int getValue() { return value; }? ? publicsynchronizedint increment() {? ? ? ? return value++;? ? }}

之所以將這種技術(shù)稱之為 “開銷較低的讀-寫鎖” 是因?yàn)槟褂昧瞬煌耐綑C(jī)制進(jìn)行讀寫操作。因?yàn)楸纠械膶懖僮鬟`反了使用 volatile 的第一個(gè)條件余蟹,因此不能使用 volatile 安全地實(shí)現(xiàn)計(jì)數(shù)器 —— 您必須使用鎖卷胯。然而,您可以在讀操作中使用 volatile 確保當(dāng)前值的可見性威酒,因此可以使用鎖進(jìn)行所有變化的操作窑睁,使用 volatile 進(jìn)行只讀操作。其中葵孤,鎖一次只允許一個(gè)線程訪問值担钮,volatile 允許多個(gè)線程執(zhí)行讀操作,因此當(dāng)使用 volatile 保證讀代碼路徑時(shí)尤仍,要比使用鎖執(zhí)行全部代碼路徑獲得更高的共享度 —— 就像讀-寫操作一樣箫津。然而,要隨時(shí)牢記這種模式的弱點(diǎn):如果超越了該模式的最基本應(yīng)用宰啦,結(jié)合這兩個(gè)競(jìng)爭(zhēng)的同步機(jī)制將變得非常困難苏遥。

結(jié)束語

與鎖相比,Volatile 變量是一種非常簡(jiǎn)單但同時(shí)又非常脆弱的同步機(jī)制赡模,它在某些情況下將提供優(yōu)于鎖的性能和伸縮性田炭。如果嚴(yán)格遵循 volatile 的使用條件 —— 即變量真正獨(dú)立于其他變量和自己以前的值 —— 在某些情況下可以使用volatile代替synchronized來簡(jiǎn)化代碼。然而漓柑,使用volatile的代碼往往比使用鎖的代碼更加容易出錯(cuò)教硫。本文介紹的模式涵蓋了可以使用volatile代替synchronized的最常見的一些用例叨吮。遵循這些模式(注意使用時(shí)不要超過各自的限制)可以幫助您安全地實(shí)現(xiàn)大多數(shù)用例,使用 volatile 變量獲得更佳性能瞬矩。

參考資料

學(xué)習(xí)

您可以參閱本文在 developerWorks 全球站點(diǎn)上的英文原文挤安。

Java Concurrency in Practice:使用 Java 代碼開發(fā)并發(fā)程序的 how-to 手冊(cè),內(nèi)容包括構(gòu)建并編寫線程安全的類和程序丧鸯、避免性能影響、管理性能和測(cè)試并發(fā)應(yīng)用程序嫩絮。

流行的原子:介紹了 Java 5.0 中新增的原子變量類丛肢,該特性對(duì) volatile 變量進(jìn)行了擴(kuò)展,從而支持原子狀態(tài)轉(zhuǎn)換剿干。

非阻塞算法簡(jiǎn)介:介紹如何使用原子變量而不是鎖實(shí)現(xiàn)并發(fā)算法蜂怎。

Volatiles:從 Wikipedia 獲得關(guān)于 volatile 變量的更多信息。

Java 技術(shù)專區(qū):提供了數(shù)百篇有關(guān) Java 編程各個(gè)方面的文章置尔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杠步,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子榜轿,更是在濱河造成了極大的恐慌幽歼,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谬盐,死亡現(xiàn)場(chǎng)離奇詭異甸私,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)飞傀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門皇型,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人砸烦,你說我怎么就攤上這事弃鸦。” “怎么了幢痘?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵唬格,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我雪隧,道長(zhǎng)西轩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任脑沿,我火速辦了婚禮藕畔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘庄拇。我一直安慰自己注服,他們只是感情好韭邓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溶弟,像睡著了一般女淑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辜御,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天鸭你,我揣著相機(jī)與錄音,去河邊找鬼擒权。 笑死袱巨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的碳抄。 我是一名探鬼主播愉老,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼剖效!你這毒婦竟也來了嫉入?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤璧尸,失蹤者是張志新(化名)和其女友劉穎咒林,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逗宁,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡映九,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞎颗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片件甥。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哼拔,靈堂內(nèi)的尸體忽然破棺而出引有,到底是詐尸還是另有隱情,我是刑警寧澤倦逐,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布譬正,位于F島的核電站,受9級(jí)特大地震影響檬姥,放射性物質(zhì)發(fā)生泄漏曾我。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一健民、第九天 我趴在偏房一處隱蔽的房頂上張望抒巢。 院中可真熱鬧,春花似錦秉犹、人聲如沸蛉谜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽型诚。三九已至客燕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狰贯,已是汗流浹背也搓。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涵紊,地道東北人还绘。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像栖袋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抚太,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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