Java并發(fā)編程之Volatile

認(rèn)真.png

該文章屬于《Java并發(fā)編程》系列文章瘾境,如果想了解更多,請(qǐng)點(diǎn)擊《Java并發(fā)編程之總目錄》

前言

在前面的文章中,我們已經(jīng)了解了Java的內(nèi)存模型阅畴,了解了其可見(jiàn)性問(wèn)題及指令重排序及Happen-Before原則柄慰,現(xiàn)在我們來(lái)了解一下關(guān)鍵字volatile嫉到。在Java中volatile可以算是Java提供的輕量級(jí)同步實(shí)現(xiàn)機(jī)制梦湘,但是在平時(shí)開(kāi)發(fā)中瞎颗,我們更多的是使用synchronized來(lái)進(jìn)行同步。對(duì)于volatile捌议,大家總是不能正確的且完整的理解哼拔。所以下面,我就和大家一起來(lái)了解一下volatile瓣颅。

volatile的作用

線程的可見(jiàn)性

當(dāng)一個(gè)變量定義為volatile后倦逐,那么該變量對(duì)所有線程都是“可見(jiàn)的”,其中“可見(jiàn)的”是指當(dāng)一條線程修改了這個(gè)變量的值弄捕,那么新值對(duì)于其他線程來(lái)說(shuō)是可以立即知道的僻孝。可能大家還是不好的理解守谓。如果你閱讀過(guò)上篇文章Java并發(fā)編程之Java內(nèi)存模型,你應(yīng)該很快的理解您单。不過(guò)沒(méi)有大礙斋荞,通過(guò)下列圖片大家應(yīng)該很快的了解。

volatile可見(jiàn)性.png

我們已經(jīng)知道在Java內(nèi)存模型中虐秦,內(nèi)存分為了線程的工作內(nèi)存及主內(nèi)存平酿。在上圖中,線程A與線程B分別從主內(nèi)存中獲取變量a(用volatile修飾)到自己的工作內(nèi)存中悦陋,也就是現(xiàn)在線程A與線程B中工作內(nèi)存中的a現(xiàn)在的變量為12蜈彼,當(dāng)線程A修改a的值為8時(shí),會(huì)將修改后的值(a=8)同步到主內(nèi)存中俺驶,同時(shí)那么會(huì)導(dǎo)致線程B中的緩存a變量的值(a=12)無(wú)效幸逆,會(huì)讓線程B重新重主內(nèi)存中獲取新的值(a=8)

volatile可見(jiàn)性的原理

在上篇文章Java并發(fā)編程之Java內(nèi)存模型中我們?cè)?jīng)講過(guò),物理計(jì)算機(jī)為了處理緩存不一致的問(wèn)題还绘。提出了緩存一致性的協(xié)議楚昭,其中緩存一致性的核心思想是:當(dāng)CPU寫(xiě)數(shù)據(jù)時(shí),如果發(fā)現(xiàn)操作的變量是共享變量拍顷,即在其他CPU中也存在該變量的副本抚太,會(huì)發(fā)出信號(hào)通知其他CPU將該變量的緩存行置為無(wú)效狀態(tài),因此當(dāng)其他CPU需要讀取這個(gè)變量時(shí)昔案,發(fā)現(xiàn)自己緩存中緩存該變量的緩存行是無(wú)效的尿贫,那么它就會(huì)從內(nèi)存重新讀取。

既然volatile修飾的變量能具有“可見(jiàn)性”踏揣,那么volatile內(nèi)部肯定是走的底層帅霜,同時(shí)也肯定滿足緩存一致性原則。因?yàn)樯婕暗降讓訁R編呼伸,這里我們不要去了解匯編語(yǔ)言身冀,我們只要知道當(dāng)用volatile修飾變量時(shí),生成的匯編指令會(huì)比普通的變量聲明會(huì)多一個(gè)Lock指令括享。那么Lock指令會(huì)在多核處理器下會(huì)做兩件事情搂根。

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)直接寫(xiě)會(huì)到系統(tǒng)內(nèi)存中(從Java內(nèi)存模型來(lái)理解,就是將線程中的工作內(nèi)存的數(shù)據(jù)直接寫(xiě)入到主內(nèi)存中)
  • 這個(gè)寫(xiě)回內(nèi)存的操作會(huì)使在其他CPU里緩存了該地址的數(shù)據(jù)無(wú)效(從Java內(nèi)存模型理解铃辖,當(dāng)線程A將工作內(nèi)存的數(shù)據(jù)修改后(新值)剩愧,同步到主內(nèi)存中,那么線程B從主內(nèi)存中初始的值(舊值)就無(wú)效了)

防止重排序

同樣的在上篇文章《Java并發(fā)編程之內(nèi)存模型》中娇斩,我們提到了為了提高CPU(處理器)的處理數(shù)據(jù)的速度仁卷,CPU(處理器)會(huì)對(duì)沒(méi)有數(shù)據(jù)依賴(lài)性的指令進(jìn)行重排序,但是CPU(處理器)的重排序會(huì)對(duì)多線程帶來(lái)問(wèn)題犬第。具體問(wèn)題我們用下列偽代碼來(lái)闡述:

public class Demo {
    private int a = 0;
    private boolean isInit = false;
    private Config config;

    public void init() {
        config = readConfig();//1
        isInit = true;//2
    }
    public void doSomething() {
        if (isInit) {//3
            doSomethingWithconfig();//4
        }
    }
}

isInit用來(lái)標(biāo)志是否已經(jīng)初始化配置锦积。其中1,2操作是沒(méi)有數(shù)據(jù)依賴(lài)性歉嗓,同理3丰介、4操作也是沒(méi)有數(shù)據(jù)依賴(lài)性的。那么CPU(處理器)可能對(duì)1鉴分、2操作進(jìn)行重排序哮幢。對(duì)3、4操作進(jìn)行重排序≈菊洌現(xiàn)在我們加入線程A操作Init()方法橙垢,線程B操作doSomething()方法,那么我們看看重排序?qū)Χ嗑€程情況下的影響伦糯。

程序執(zhí)行順序.png

上圖中2操作排在了1操作前面柜某。當(dāng)CPU時(shí)間片轉(zhuǎn)到線程B嗽元。線程B判斷 if (isInit)為true,接下來(lái)接著執(zhí)行 doSomethingWithconfig(),但是我們Config還沒(méi)有初始化。所以在多線程的情況下莺琳。重排序會(huì)影響程序的執(zhí)行結(jié)果还棱。所以為了防止重排序帶來(lái)的問(wèn)題。Java內(nèi)存模型規(guī)定了使用volatile來(lái)修飾相應(yīng)變量時(shí)惭等,可以防止CPU(處理器)在處理指令的時(shí)候禁止重排序珍手。具體如下圖所示。

public class Demo {
    private int a = 0;
    private volatile boolean isInit = false;
    private Config config;
     public void init() {
        config = readConfig();//1
        isInit = true;//2
    }
    public void doSomething() {
        if (isInit) {//3
            doSomethingWithconfig();//4
        }
    }
}

volatile防止重排序規(guī)則

那么為了處理CPU重排序的問(wèn)題辞做。Java定義了以下規(guī)則防止CPU的重排序琳要。

volatile重排序規(guī)則.png

從上表我們可以看出

  • 當(dāng)?shù)诙€(gè)操作是volatile寫(xiě)時(shí),不管第一個(gè)操作是什么秤茅,都不能重排序稚补,這個(gè)規(guī)則確保voatile寫(xiě)之前的操作不會(huì)被編譯器排序到volatile之后。
  • 當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí)框喳,不管第二個(gè)操作是什么课幕,都不能重排序。這個(gè)規(guī)則確保volatile讀之后的操作不會(huì)被編譯器重排序到volatile讀之前五垮。
  • 當(dāng)?shù)谝粋€(gè)操作是volatile寫(xiě)乍惊,第二個(gè)操作如果是volatile讀或volatile寫(xiě)時(shí),不能進(jìn)行重排序放仗。

volatile防止重排序原理

為了具體實(shí)現(xiàn)上訴我們提到的重排序規(guī)則润绎,在Java中對(duì)于volatile修飾的變量,編譯器在生成字節(jié)碼時(shí)诞挨,會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止特定類(lèi)型的處理器重排序問(wèn)題莉撇。在了解內(nèi)存屏障之前,我們先復(fù)習(xí)之前的主內(nèi)存與工作內(nèi)存交互的8種原子操作惶傻,因?yàn)閮?nèi)存屏障主要是對(duì)Java內(nèi)存模型的幾種原子操作進(jìn)行限制的棍郎。具體內(nèi)存8種原子操作,如下圖所示:

8種操作.png

上述8中原子操作中达罗,我們所涉及的是store與load操作坝撑,如果需要了解剩余6種操作,請(qǐng)參看上篇文章Java并發(fā)編程之Java內(nèi)存模型粮揉。

這里對(duì)內(nèi)存屏障所涉及到的兩種操作進(jìn)行解釋?zhuān)?/p>

  • load:作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入到工作內(nèi)存變量副本中抚笔。
  • store:作用于工作內(nèi)存的變量扶认,它把工作內(nèi)存中一個(gè)變量值傳送到主內(nèi)存中。以便隨后的write操作殊橙。
內(nèi)存屏障插入策略

下面是基于volatile修飾的變量辐宾,編譯器在指令序列插入的內(nèi)存屏障保守插入策略如下:

  • 在每個(gè)volatile寫(xiě)操作的前面插入一個(gè)storestore屏障狱从。
  • 在每個(gè)volatile寫(xiě)操作的后面插入一個(gè)storeload屏障。
  • 在每個(gè)volatile讀操作的后面插入一個(gè)loadload屏障叠纹。
  • 在每個(gè)volatile讀操作的后面插入一個(gè)loadstore屏障季研。

volatile寫(xiě)內(nèi)存屏障

volatile寫(xiě)屏障.png
  • storestore屏障:對(duì)于這樣的語(yǔ)句store1; storestore; store2,在store2及后續(xù)寫(xiě)入操作執(zhí)行前誉察,保證store1的寫(xiě)入操作對(duì)其它處理器可見(jiàn)与涡。(也就是說(shuō)如果出現(xiàn)storestore屏障,那么store1指令一定會(huì)在store2之前執(zhí)行持偏,CPU不會(huì)store1與store2進(jìn)行重排序)
  • storeload屏障:對(duì)于這樣的語(yǔ)句store1; storeload; load2驼卖,在load2及后續(xù)所有讀取操作執(zhí)行前,保證store1的寫(xiě)入對(duì)所有處理器可見(jiàn)鸿秆。(也就是說(shuō)如果出現(xiàn)storeload屏障酌畜,那么store1指令一定會(huì)在load2之前執(zhí)行,CPU不會(huì)對(duì)store1與load2進(jìn)行重排序)

volatile讀內(nèi)存屏障

volatile讀屏障.png
  • loadload屏障:對(duì)于這樣的語(yǔ)句load1; loadload; load2,在load2及后續(xù)讀取操作要讀取的數(shù)據(jù)被訪問(wèn)前卿叽,保證load1要讀取的數(shù)據(jù)被讀取完畢桥胞。(也就是說(shuō),如果出現(xiàn)loadload屏障考婴,那么load1指令一定會(huì)在load2之前執(zhí)行贩虾,CPU不會(huì)對(duì)load1與load2進(jìn)行重排序)
  • loadstore屏障:對(duì)于這樣的語(yǔ)句load1; loadstore; store2,在store2及后續(xù)寫(xiě)入操作被刷出前蕉扮,保證load1要讀取的數(shù)據(jù)被讀取完畢整胃。(也就是說(shuō),如果出現(xiàn)loadstore屏障喳钟,那么load1指令一定會(huì)在store2之前執(zhí)行屁使,CPU不會(huì)對(duì)load1與store2進(jìn)行重排序)

編譯器內(nèi)存屏障的優(yōu)化

上面我們講到了在插入內(nèi)存屏障時(shí),編譯器如果采用保守策略的情況下奔则,分別會(huì)在volatile寫(xiě)與volatile讀插入不同的內(nèi)存屏障蛮寂,那現(xiàn)在我們來(lái)看一下,在實(shí)際開(kāi)發(fā)中易茬,編譯器在使用內(nèi)存屏障時(shí)的優(yōu)化酬蹋。

public class VolatileBarrierDemo {
    int a;
    volatile int v1 = 1;
    volatile int v2 = 2;

    public void readAndWrite() {
        int i = v1;//第一個(gè)volatile讀
        int j = v2;//第二個(gè)volatile讀
        a = i + j;//普通寫(xiě)
        v1 = i + 1;//第一個(gè)volatile寫(xiě)
        v2 = j * 2;//第二個(gè)volatile寫(xiě)
    }
}

那么針對(duì)上述代碼,我們生成相應(yīng)的屏障(圖片在手機(jī)端觀看可能會(huì)不太清除抽莱,建議在pc端上觀看)

屏障優(yōu)化圖.png

觀察上圖范抓,我們發(fā)現(xiàn),在編譯器生成屏障時(shí)食铐,省略了第一個(gè)volatile讀下的loadstore屏障匕垫,省略了第二個(gè)volatile讀下的loadload屏障,省略了第一個(gè)volatile寫(xiě)下的storeload屏障虐呻。結(jié)合上訴我們所講的loadstore屏障象泵、loadload屏障寞秃、storeload屏障下的語(yǔ)義,我們能得到省略以下屏障的原因偶惠。

  • 省略第一個(gè)volatile讀下的loadstore屏障:因?yàn)榈谝粋€(gè)volatile讀下的下一個(gè)操作是第二個(gè)volatile的讀春寿,并不涉及到寫(xiě)的操作(也就是store)。所以可以省略忽孽。
  • 省略第二個(gè)volatile讀下的loadload屏障:因?yàn)榈诙€(gè)volatile讀的下一個(gè)操作是普通寫(xiě)绑改,并不涉及到讀的操作(也就是load)。所以可以省略
  • 省略第一個(gè)volatile寫(xiě)下的storeload屏障:因?yàn)榈谝粋€(gè)volatile寫(xiě)的下一個(gè)操作是第二個(gè)volatile的寫(xiě)扒腕,并不涉及到讀的操作(也就是load)绢淀。所以可以省略。

其中大家要注意的是瘾腰,優(yōu)化結(jié)束后的storeload屏障時(shí)不能省略的皆的,因?yàn)樵诘诙€(gè)volatile寫(xiě)之后,方法理解return,此時(shí)編譯器可能無(wú)法確定后面是否會(huì)有讀寫(xiě)操作蹋盆,為了安全起見(jiàn)费薄,編譯器通常會(huì)在這里加入一個(gè)storeload屏障。

處理器內(nèi)存屏障的優(yōu)化

上面我們講了編譯器在生成屏障的時(shí)候栖雾,會(huì)根據(jù)程序的邏輯操作省略不必要的內(nèi)存屏障楞抡。但是由于不同的處理器有不同的“松耦度”的內(nèi)存模型,內(nèi)存屏障的優(yōu)化根據(jù)不同的處理器有著不同的優(yōu)化方式析藕。以x86處理器為例召廷。針對(duì)我們上面所描述的編譯器內(nèi)存屏障優(yōu)化圖。在x86處理器中账胧,除最后的storeload屏障外竞慢,其他的屏障都會(huì)省略。

x86處理器優(yōu)化后.png

x86處理器與其他處理器的內(nèi)存屏障的優(yōu)化治泥,這里不過(guò)的描述筹煮,有興趣的小伙伴可以查閱相關(guān)資料繼續(xù)研究。

volatile的使用注意事項(xiàng)

在volatile使用的時(shí)候居夹,需要注意volatile只保證可見(jiàn)性败潦,并不能保證原子性,這里所提到的原子性需要給大家補(bǔ)充一個(gè)知識(shí)點(diǎn)准脂。

原子性定義

在Java中劫扒,對(duì)基本的數(shù)據(jù)類(lèi)型的變量的訪問(wèn)和讀寫(xiě)操作都是原子性操作,且這些操作在CPU中不可以在中途暫停然后再調(diào)度狸膏,既不被中斷操作粟关,要不執(zhí)行完成,要不就不執(zhí)行环戈。

直接通過(guò)定義來(lái)理解確實(shí)比較困難闷板,通過(guò)下面這個(gè)例子,讓我們一起來(lái)了解院塞。

x = 10;         //語(yǔ)句1
x++;           //語(yǔ)句2
x = x + 1;     //語(yǔ)句3

大家可以來(lái)猜一猜遮晚,以上3個(gè)語(yǔ)句有哪些是具有原子性呢。好了拦止。我告訴答案吧县遣,只有語(yǔ)句1具有原子性。大家對(duì)此會(huì)感到很疑惑汹族。

對(duì)于語(yǔ)句1:是直接將數(shù)值10賦給x,也就是直接將數(shù)值10賦值到工作內(nèi)存中萧求。
對(duì)于語(yǔ)句2:先去讀取x的值,然后計(jì)算x加上1后的值顶瞒,最后將計(jì)算后的值賦值給x夸政,
對(duì)于語(yǔ)句3:同語(yǔ)句3。

對(duì)于語(yǔ)句2榴徐,3因?yàn)樯婕暗蕉鄠€(gè)操作守问,且在多線程的情況下,CPU可以進(jìn)行時(shí)間片的切換操作(也就是可以暫停在某個(gè)操作后)坑资。那么就可能出現(xiàn)線程安全的問(wèn)題耗帕。

volatile為什么不具備原子性

描述了原子性后,相信大家都會(huì)有個(gè)疑問(wèn)“volatile不具備原子性有什么關(guān)系呢袱贮?其實(shí)原因很簡(jiǎn)單仿便,雖然volatile是具備可見(jiàn)性的(也就是指當(dāng)一條線程修改了這個(gè)變量的值,那么新值對(duì)于其他線程來(lái)說(shuō)是可以立即知道的)攒巍,但是對(duì)于該變量有可能有多個(gè)操作例如上文提到的x++嗽仪。那么在有多個(gè)操作的情況下,CPU任然可以先暫停然后在調(diào)度的窑业。既然能被暫停后繼續(xù)在調(diào)度钦幔,那么volatile肯定是不具備原子性的了。

volatile的使用場(chǎng)景

現(xiàn)在我們已經(jīng)了解了volatile的相關(guān)特性常柄,那么就來(lái)說(shuō)說(shuō)鲤氢,volatile的具體使用場(chǎng)景,因?yàn)関olatie變量只能保證可見(jiàn)性西潘,并不能保證原子性卷玉,所以在輕量級(jí)線程同步中我們可以使用volatile關(guān)鍵字。但是有兩個(gè)前提條件:

  • 第一個(gè)條件:運(yùn)算結(jié)果并不依賴(lài)變量的當(dāng)前值喷市,或者能夠確保只有單一的線程修改變量的值相种。
  • 第二個(gè)條件:變量不需要與其他的狀態(tài)變量共同參與不變約束。

直接理解上述兩個(gè)條件品姓,可能會(huì)有點(diǎn)困難寝并,下面分別對(duì)著兩個(gè)前提條件進(jìn)行解釋?zhuān)?/p>

針對(duì)第一個(gè)條件

  volatile int a  = 0;
    //在多線程情況下錯(cuò)誤箫措,在單線程情況下正確的方式
    public void doSomeThingA() {
    //在單線程情況下,不會(huì)出現(xiàn)線程安全的問(wèn)題衬潦,正確
    //在多線程情況下斤蔓,a最終的值依賴(lài)于當(dāng)前a的值,錯(cuò)誤
         a++;     
    }
    //正確的使用方式
    public void doSomeThingB() {
        //不管是在單線程還是多線程的情況下镀岛,都不會(huì)出現(xiàn)線程安全的問(wèn)題
        if(a==0){
         a = 1;
        }
    }

在上述偽代碼中弦牡,我們能明確的看出,只要volatile修飾的變量不涉及與運(yùn)算結(jié)果的依賴(lài)漂羊,那么不管是在多線程驾锰,還是單線程的情況下,都是正確的走越。當(dāng)然我這里只是將a變量定義成成int椭豫,對(duì)于其他剩下的基礎(chǔ)類(lèi)型數(shù)據(jù)也是適用的。

針對(duì)第二個(gè)條件

其實(shí)理解第二個(gè)條件买喧,大家可以反過(guò)來(lái)理解捻悯,即使用volatile的變量不能包含在其他變量的不變式中,下面?zhèn)未a將會(huì)通過(guò)反例說(shuō)明:

    private volatile int lower;
    private volatile int 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;  
    }  
}

在上述代碼中淤毛,我們明顯發(fā)現(xiàn)其中包含了一個(gè)不變式 —— 下界總是小于或等于上界(也就是lower<=upper)今缚。那么在多線程的情況下,兩個(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ì)通過(guò)用于保護(hù)不變式的檢查餐塘,使得最后的范圍值是(4, 3)。很顯然這個(gè)結(jié)果是錯(cuò)誤的皂吮。

總結(jié)

  • volatile具有可見(jiàn)性不具有原子性戒傻,同時(shí)能防止指令重排序。
  • volatile之所以具有可見(jiàn)性蜂筹,是因?yàn)榈讓又械腖ock指令需纳,該指令會(huì)將當(dāng)前處理器緩存行的數(shù)據(jù)直接寫(xiě)會(huì)到系統(tǒng)內(nèi)存中,且這個(gè)寫(xiě)回內(nèi)存的操作會(huì)使在其他CPU里緩存了該地址的數(shù)據(jù)無(wú)效艺挪。
  • volatile之所以能防止指令重排序不翩,是因?yàn)镴ava編譯器對(duì)于volatile修飾的變量,會(huì)插入內(nèi)存屏障。內(nèi)存屏障會(huì)防止CPU處理指令的時(shí)候重排序的問(wèn)題
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末口蝠,一起剝皮案震驚了整個(gè)濱河市器钟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亚皂,老刑警劉巖俱箱,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灭必,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)乃摹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)禁漓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人孵睬,你說(shuō)我怎么就攤上這事播歼。” “怎么了掰读?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵秘狞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蹈集,道長(zhǎng)烁试,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任拢肆,我火速辦了婚禮减响,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘郭怪。我一直安慰自己支示,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布鄙才。 她就那樣靜靜地躺著颂鸿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攒庵。 梳的紋絲不亂的頭發(fā)上嘴纺,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音叙甸,去河邊找鬼颖医。 笑死,一個(gè)胖子當(dāng)著我的面吹牛裆蒸,可吹牛的內(nèi)容都是我干的熔萧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼佛致!你這毒婦竟也來(lái)了贮缕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤俺榆,失蹤者是張志新(化名)和其女友劉穎感昼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體罐脊,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡定嗓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了萍桌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宵溅。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖上炎,靈堂內(nèi)的尸體忽然破棺而出恃逻,到底是詐尸還是另有隱情,我是刑警寧澤藕施,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布寇损,位于F島的核電站,受9級(jí)特大地震影響裳食,放射性物質(zhì)發(fā)生泄漏矛市。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一胞谈、第九天 我趴在偏房一處隱蔽的房頂上張望尘盼。 院中可真熱鬧,春花似錦烦绳、人聲如沸卿捎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)午阵。三九已至,卻和暖如春享扔,著一層夾襖步出監(jiān)牢的瞬間底桂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工惧眠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留籽懦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓氛魁,卻偏偏與公主長(zhǎng)得像暮顺,于是被迫代替她去往敵國(guó)和親厅篓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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