Java虛擬機(jī)(二):Java內(nèi)存模型

1 基本概念

在上一篇文章Java內(nèi)存區(qū)域 中纤壁,我們講了JVM為了更好的管理內(nèi)存,將Java進(jìn)程的內(nèi)存劃分成了幾個(gè)功能榕订、用途不同的區(qū)域析显,所以很多人會(huì)認(rèn)為劃分后的內(nèi)存布局就是Java內(nèi)存模型。嚴(yán)格來說肚豺,這個(gè)說法是不準(zhǔn)確的溃斋,不過大家在交流的時(shí)候直接說成內(nèi)存模型好像也無傷大雅。那究極什么才是嚴(yán)格意義上的Java內(nèi)存模型呢吸申?

Java內(nèi)存模型(Java Memory Model梗劫,簡(jiǎn)稱JMM)本身是一個(gè)抽象的概念,不是真實(shí)存在的截碴,它描述的是一組規(guī)則梳侨,Java內(nèi)存訪問內(nèi)存都需要遵循這組規(guī)則。在深入了解之前日丹,我們先來看看JMM里有幾個(gè)基本概念:

  • 工作內(nèi)存走哺。由于Java程序是單進(jìn)程程序,故Java并發(fā)大多值的都是線程級(jí)別的并發(fā)哲虾,即線程是程序執(zhí)行的最小單位丙躏。JMM規(guī)定了每個(gè)線程都有一個(gè)屬于自己的工作內(nèi)存择示,線程對(duì)本地局部變量的操作都直接在工作線程上執(zhí)行,而對(duì)共享變量的操作需先從主內(nèi)存(馬上就介紹主內(nèi)存)中拷貝一份到工作內(nèi)存晒旅,然后在工作內(nèi)存中對(duì)該變量進(jìn)行操作栅盲,完成之后再寫回主內(nèi)存。
  • 主內(nèi)存废恋。主內(nèi)存主要存儲(chǔ)的是實(shí)例對(duì)象谈秫,所有線程創(chuàng)建的實(shí)例對(duì)象都存儲(chǔ)在主內(nèi)存中(這句話在新版本的Java中會(huì)不太準(zhǔn)確,因?yàn)樵谛碌腏VM中鱼鼓,有些特殊情況會(huì)使得對(duì)象實(shí)例被分配在棧上拟烫,成為線程私有的實(shí)例對(duì)象)。主內(nèi)存還包括了一些常量蚓哩,靜態(tài)變量构灸,類的元信息等,總之岸梨,主內(nèi)存就是被多個(gè)線程共享的內(nèi)存喜颁。

其大致結(jié)構(gòu)可以看看下圖:

借用了CSDN博主@zejian_ 的圖片

根據(jù)虛擬機(jī)規(guī)范,對(duì)于一個(gè)實(shí)例的方法曹阔,如果該方法包含的本地變量(包括參數(shù))是基本數(shù)據(jù)類型半开,那么對(duì)應(yīng)的值將被存儲(chǔ)在工作內(nèi)存中(也可以理解為在虛擬機(jī)棧幀中),如果是引用類型赃份,那么引用本身也會(huì)被存儲(chǔ)在工作內(nèi)存中寂拆,而其指向的實(shí)例對(duì)象會(huì)被存儲(chǔ)在主內(nèi)存中,被各個(gè)線程共享抓韩。而對(duì)于實(shí)例的字段纠永,無論是基本類型還是引用類型,都會(huì)直接存儲(chǔ)到主內(nèi)存中谒拴。如下圖所示:

了解了上述內(nèi)容尝江,我們知道實(shí)例對(duì)象在JMM的控制下是存儲(chǔ)在主內(nèi)存的,也就是被多個(gè)線程共享的英上,每個(gè)線程要操作對(duì)象就必須拷貝一份到工作內(nèi)存中炭序,然后進(jìn)行操作,最后再寫回內(nèi)存苍日。我想說到這了惭聂,大家不難看出這就是導(dǎo)致線程安全問題的原因,關(guān)于線程安全問題相恃,我的博客里有一些文章辜纲,大家可以去看看。

2 為什么要有Java內(nèi)存模型

JMM只是一組規(guī)則,并不是真實(shí)存在的侨歉。即使上面的圖畫得再漂亮屋摇,在底層都只是一塊內(nèi)存(即使現(xiàn)在的個(gè)人計(jì)算機(jī)都能插多個(gè)內(nèi)存條揩魂,但是操作系統(tǒng)還是把他們抽象成一整塊連續(xù)的存儲(chǔ))幽邓。那這組規(guī)則是什么呢?上面我提到過JVM把內(nèi)存劃分成了可共享區(qū)域和線程私有區(qū)域火脉,這回導(dǎo)致線程安全問題牵舵,JMM的存在就是為了解決這個(gè)問題。

這里我不得不再次說一下:JMM只是一組規(guī)則倦挂,JVM將內(nèi)存劃分為幾個(gè)區(qū)域畸颅,分為線程私有的和線程共享的區(qū)域,而作為工作內(nèi)存和主內(nèi)存其實(shí)還是這些區(qū)域方援,也就是說它們主內(nèi)存没炒、工作內(nèi)存和方法區(qū)、虛擬機(jī)棧犯戏、程序計(jì)數(shù)器送火、本地方法棧、堆等式有交叉關(guān)系的先匪。JMM之所以再做抽象种吸,分為主內(nèi)存和工作內(nèi)存,主要目的就是為了更好的描述這組規(guī)則呀非。

JMM定義了一組規(guī)則坚俗,通過這組規(guī)則來決定一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)另一個(gè)線程可見。JMM是圍繞程序執(zhí)行的原子性岸裙,可見性和順序性來展開的猖败,下面我們來一一分析。

2.1 原子性

原子性指的是一個(gè)或者一組操作即使在多線程環(huán)境下也不可被中斷降允,一旦開始就不能被其他線程所影響恩闻,即一旦操作開始,那么直到該操作結(jié)束拟糕,CPU都不可以被其他線程占用判呕。這是一個(gè)很重要的特性,至于如何做到送滞,我這里簡(jiǎn)單大致的說一下:我們知道線程的執(zhí)行是需要CPU做調(diào)度的侠草,引發(fā)線程切換的因素有多個(gè),例如當(dāng)前線程時(shí)間片用完了犁嗅、被阻塞了等等边涕,但總歸來說,他們被切換的根本原因就是發(fā)生了中斷,所以功蜓,我們可以在操作準(zhǔn)備開始的時(shí)候园爷,屏蔽中斷,結(jié)束的時(shí)候再打開中斷式撼,這樣就實(shí)現(xiàn)了原子性童社。

我想,通過上面的描述應(yīng)該不難理解原子性對(duì)于線程安全的作用了吧著隆。通過保證操作的原子性扰楼,就可以避免其他線程的干擾,從而保證線程安全美浦。例如JDK里有java.util.concurrent.atomic包弦赖,該包下有很多Atomic打頭的類,通常我們稱作“原子類”浦辨。使用這些類可以很輕松的解決一部分線程安全問題蹬竖,例如在并發(fā)環(huán)境下做計(jì)數(shù):

public class Counter {
    
    //使用原生類型,在并發(fā)環(huán)境下會(huì)發(fā)生線程安全問題
    //private static int counter = 0;
    
    //使用原子類可以保證線程安全
    private static final AtomicInteger counter = new AtomicInteger(0);
    
    public void addCount() {
        counter.getAndIncrement();
    }
}

除了使用“原子類”流酬,一般還使鎖來保證原子性币厕,線程執(zhí)行操作之前需要先獲取鎖,操作完成之后需要釋放鎖康吵。在Java里劈榨,鎖有內(nèi)置鎖和顯式鎖,內(nèi)置鎖就是synchronized晦嵌,這是一個(gè)可重入鎖同辣,同一線程不需要重復(fù)獲取鎖。顯式鎖就是 java.util.concurrent.locks 包下的相關(guān)類惭载,例如 ReentrantLock旱函,ReadWriteLock ,ReentrantReadWriteLock等描滔。

2.2 可見性

可見性即當(dāng)一個(gè)線程修改了某個(gè)共享變量的值棒妨,其他線程能夠馬上得知這個(gè)修改的值。對(duì)于串行程序含长,可見性是沒什么意義的券腔,因?yàn)閱尉€程環(huán)境下程序是順序執(zhí)行的(在并發(fā)環(huán)境下,每個(gè)線程執(zhí)行也是順序執(zhí)行的拘泞,但是因?yàn)闀r(shí)序的問題纷纫,所以整體看起來就不是順序執(zhí)行的了),不存在修改無法得知的情況陪腌。Java提供了volatile關(guān)鍵字來保證可見性辱魁,在這里先不說烟瞧,文章后面會(huì)詳細(xì)講到volatile。

2.3 順序性

在可見性那里提到過一些染簇,順序性指的程序的執(zhí)行是按照順序有序執(zhí)行的参滴。在單線程環(huán)境下,確實(shí)如此锻弓,沒有毛病砾赔。但是到多線程的環(huán)境下,對(duì)于每個(gè)線程自己來說弥咪,自己本身確實(shí)是順序執(zhí)行的过蹂,這也沒毛病聚至,但是如果一個(gè)線程觀察另一個(gè)線程甚亭,那么所有的操作都是無序的。

2.4 happens-before原則

除了使用鎖來保證原子性和使用volatile之外,在JMM中,還提供了happens-before原則來輔助我們。我們可以在happens-before前后加一些詞語來修飾枯冈,這樣會(huì)便于理解誉尖。即“在同一個(gè)線程中丢间,書寫在前面的操作happens-before書寫在后面的操作”饮六。happens-before原則共有8個(gè)臂外,如下:

  • 單線程happen-before原則:在同一個(gè)線程中,書寫在前面的操作happen-before后面的操作。
  • 鎖的happen-before原則:同一個(gè)鎖的unlock操作happen-before此鎖的lock操作谭溉。
  • volatile的happen-before原則:對(duì)一個(gè)volatile變量的寫操作happen-before對(duì)此變量的任意操作(當(dāng)然也包括寫操作了)柜与。
  • happen-before的傳遞性原則:如果A操作 happen-before B操作迁匠,B操作happen-before C操作,那么A操作happen-before C操作。
  • 線程啟動(dòng)的happen-before原則:同一個(gè)線程的start方法happen-before此線程的其它方法愿卸。
  • 線程中斷的happen-before原則:對(duì)線程interrupt方法的調(diào)用happen-before被中斷線程的檢測(cè)到中斷發(fā)送的代碼灵临。
  • 線程終結(jié)的happen-before原則:線程中的所有操作都happen-before線程的終止檢測(cè)。
  • 對(duì)象創(chuàng)建的happen-before原則:一個(gè)對(duì)象的初始化完成先于他的finalize方法調(diào)用趴荸。

happens-before原則主要是輔助我們判斷代碼是否是線程安全的儒溉,如果以上原則任何一個(gè)都不滿足就意味著我們應(yīng)該重新審視一下代碼,并作出想應(yīng)修改來保證代碼在并發(fā)環(huán)境下的線程安全赊舶。關(guān)于happens-before更多的解釋睁搭,網(wǎng)上有不少好文章,在此不再贅述笼平。

3 volatile

現(xiàn)在來看看volatile關(guān)鍵字,在Java并發(fā)程序中舔痪,經(jīng)常能看到volatile的身影寓调,但也容易被濫用。volatile是JVM提供的輕量級(jí)同步機(jī)制锄码,主要有兩個(gè)作用:

  • 保證可見性夺英,當(dāng)一個(gè)線程修改了有volatile修飾的變量,這個(gè)修改會(huì)立刻反應(yīng)到主內(nèi)存中滋捶,換句話說痛悯,當(dāng)其他線程訪問該變量時(shí),總是會(huì)得到新的值重窟。
  • 禁止指令重排载萌。

3.1 保證可見性

可見性上面已經(jīng)說過了,在此說說訪問volatile的流程巡扇,理解了流程扭仁,就能理解為什么volatile能保證可見性了。我們知道厅翔,每個(gè)線程都有自己的工作內(nèi)存乖坠,操作變量的時(shí)候需要先到主內(nèi)存復(fù)制一份拷貝到工作內(nèi)存中,完成操作后再寫回主內(nèi)存刀闷,在線程寫回主內(nèi)存之前熊泵,其他線程是無法得知修改的仰迁,這就造成了其他線程有可能讀取到的值是一個(gè)過期無效的值,從而導(dǎo)致線程安全問題顽分。而有volatile修飾的變量稍有不同徐许,線程在對(duì)volatile變量進(jìn)行修改的時(shí)候,完事之后會(huì)立即刷新到主內(nèi)存中怯邪,其他線程讀取的時(shí)候也會(huì)被迫去主內(nèi)存中取值绊寻。從宏觀上看,就好像其他線程能看到當(dāng)前線程修改之后的值一樣悬秉,這就保證了可見性澄步。

3.2 禁止指令重排

指令重排是編譯器的優(yōu)化操作,編譯器可能會(huì)對(duì)一些沒有依賴關(guān)系代碼做重新排序和泌,導(dǎo)致編譯后的代碼和我們編寫的代碼順序上有一些差異(說到這村缸,我想起了JS的變量提升,將一些沒有依賴關(guān)系的變量聲明提升到代碼頂端)武氓,如下所示:

int x = 1; //1
System.out.Println(x); //2
int y = 2; //3

如果允許編譯器做指令重排梯皿,那么編譯后的代碼順序可能是下面這樣的:

int x = 1;  //1
int y = 2; //3
System.out.Println(x); //2

這就是Java的指令重排。

為什么需要做指令重排呢县恕?編譯器既然做了东羹,就肯定是有原因的,要不然費(fèi)這勁干哈忠烛!因?yàn)橹嘏判蛑笥欣谥噶畹膱?zhí)行属提,從而提供程序的性能。CPU執(zhí)行指令是采用流水線的方式美尸,這種方式可以提高CPU的利用率冤议,在CPU執(zhí)行指令的時(shí)候有可能會(huì)因?yàn)橐蕾囮P(guān)系而出現(xiàn)“停頓”,這將使得CPU在這個(gè)時(shí)鐘周期內(nèi)無事可干师坎,導(dǎo)致CPU的利用率降低恕酸,程序總體性能會(huì)受到影響。指令重排后胯陋,會(huì)處理這些依賴關(guān)系蕊温,最終會(huì)減少CPU停頓次數(shù),最好的情況下完全消除停頓惶岭,使得CPU利用率最大化寿弱。關(guān)于指令重排的更加詳細(xì)的解釋,可以看看全面理解Java內(nèi)存模型(JMM)及volatile關(guān)鍵字 這篇文章的指令重排部分按灶,該博主解釋的非常好症革,清晰易懂,推薦多多關(guān)注鸯旁。

在單線程環(huán)境下噪矛,指令重排當(dāng)然沒問題量蕊,但是在多線程并發(fā)環(huán)境下,指令重排可能會(huì)導(dǎo)致線程安全問題艇挨。就拿面試巢信冢考的單例模式來講吧,單例模式至少有7種寫法缩滨,我們來看看雙重檢查鎖的寫法(Double-Check Lock势就,簡(jiǎn)稱DCL):

public class DCL {

    private static DCL instance;

    private DCL() {

    }

    public static DCL getInstance() {
        if (instance == null) {
            synchronized (DCL.class) {
                if (instance == null) {
                    instance = new DCL();
                }
            }
        }
        return instance;
    }
}

私有字段、私有構(gòu)造函數(shù)脉漏,公有的靜態(tài)方法獲取實(shí)例苞冯,整個(gè)類只有一個(gè)入口點(diǎn),好像沒什么問題侧巨。在靜態(tài)方法里舅锄,先判斷instance是否為null,不為null就直接返回司忱,為null再進(jìn)入if邏輯里皇忿,然后用內(nèi)置鎖鎖住整個(gè)類,開辟了一個(gè)臨界區(qū)坦仍,其他線程此時(shí)就不能訪問了鳍烁,當(dāng)前線程再判斷instance是否為null,之所以要再次判斷是因?yàn)榫€程進(jìn)入臨界區(qū)之前繁扎,進(jìn)入第一個(gè)if邏輯之后可能會(huì)被其他線程搶占CPU老翘,其他線程有可能獲取到鎖并完成了對(duì)instance的初始化,為了防止這種情況锻离,在里面再做一次if判斷來保證不會(huì)重復(fù)初始化instance,這就是雙重檢查這個(gè)名字的由來墓怀。

但是汽纠,這樣真的沒問題嗎?答案是不傀履!這里有可能會(huì)因?yàn)橹噶钪嘏艑?dǎo)致獲取到的值是null虱朵。instance = new DCL()不是一個(gè)原子操作,而是分三步操作:

  1. 為對(duì)象分配內(nèi)存空間
  2. 初始化對(duì)象
  3. 將instance引用指向剛剛分配的地址

這里第2步和第3步?jīng)]有依賴關(guān)系钓账,所以編譯器在做指令重排的時(shí)候可能會(huì)將2和3的順序做一個(gè)調(diào)換碴犬,變成這樣:

  1. 為對(duì)象分配內(nèi)存空間
  2. 將instance引用指向剛剛分配的地址
  3. 初始化對(duì)象

這就可能導(dǎo)致一種情況,當(dāng)前線程執(zhí)行到“將instance引用指向剛剛分配的地址”這一步梆暮,此時(shí)被其他線程搶占CPU服协,其他線程進(jìn)入方法,做第一個(gè)if判斷啦粹,此時(shí)的結(jié)果會(huì)是false偿荷,然后就直接走到方法最后返回instance了窘游,但此時(shí)instance沒有初始化完成,也就是說此時(shí)的instance是無效的跳纳!為了解決這個(gè)問題忍饰,我們可以給instance添加volatile關(guān)鍵字,此時(shí)volatile關(guān)鍵字的作用就是禁止指令重排寺庄,這樣就解決了這個(gè)問題艾蓝。

4 final

final除了用來約束常量,使方法不能被重寫斗塘,類不能繼承之外赢织。還有一些規(guī)則,規(guī)則主要有兩個(gè):

  • final寫:“構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final的寫入”與“之后把這個(gè)被構(gòu)造對(duì)象賦值給其他引用”之間不能重排序逛拱。
  • final讀:“初次讀一個(gè)包含final字段的對(duì)象”與“之后初次讀該對(duì)象的final字段”之間不能重排序敌厘。

對(duì)于寫來說,如果一個(gè)final字段在構(gòu)造函數(shù)內(nèi)才被寫入值朽合,那么這個(gè)寫入操作必須要發(fā)生在把構(gòu)造完成的對(duì)象賦值給其他引用之前俱两。例如在多線程環(huán)境下,A線程調(diào)用構(gòu)造函數(shù)曹步,在構(gòu)造函數(shù)里對(duì)final字段進(jìn)行寫入操作宪彩,B線程不能提前把該對(duì)象實(shí)例賦值給其他引用,即保證final字段一定先被初始化讲婚。

這里需要注意尿孔,我們不能將final應(yīng)用到上述的DCL類,雖然final的寫規(guī)則確實(shí)能防止instance = new DCL()的三個(gè)步驟的重排序筹麸,但是并不適用于DCL類活合,因?yàn)閒inal字段要么在聲明的時(shí)候直接寫入,要么在初始化塊或者構(gòu)造函數(shù)類寫入物赶,顯然DCL類不符合這個(gè)規(guī)則白指。關(guān)于使用final的例子,可以看看單例模式的“懶漢”形式酵紫,“懶漢”形式之所以是線程安全的告嘲,就是因?yàn)閒inal的這個(gè)規(guī)則。

對(duì)于讀來說奖地,初次讀包含final字段的對(duì)象和初次讀該對(duì)象的fianl字段之間存在間接的依賴關(guān)系橄唬,這個(gè)final讀規(guī)則就保證了要讀某個(gè)對(duì)象的final域,必須寫讀這個(gè)包含這個(gè)fianl字段的對(duì)象参歹,這個(gè)規(guī)則比較自然仰楚,我們覺得這應(yīng)該是理所當(dāng)然的。大多數(shù)編譯器也確實(shí)不會(huì)對(duì)他們做重排序,所以缸血,這個(gè)規(guī)則就是用來處理那些比較“皮”的編譯器蜜氨。

final字段也不應(yīng)該發(fā)生“逸出”,這其實(shí)主要是針對(duì)final字段是引用類型捎泻。換句話說飒炎,final字段在構(gòu)造函數(shù)執(zhí)行期間,不應(yīng)該被其他線程訪問到笆豁,否則上述規(guī)則就都沒有了意義郎汪。

5 小結(jié)

Java內(nèi)存模型不同于Java內(nèi)存區(qū)域的劃分,Java內(nèi)存模型描述的是一組規(guī)則闯狱,是一個(gè)抽象的概念煞赢,工作內(nèi)存、主內(nèi)存什么的都是抽象出來的哄孤,并不是真實(shí)存在的照筑,只是為了更好的描述規(guī)則而已。JMM的規(guī)則主要是圍繞原子性瘦陈,可見性和順序性來展開的凝危,理解這三個(gè)性質(zhì)可以更好的理解JMM。保證原子性可以采取鎖等同步手段晨逝,保證可見性可以利用volatile蛾默。volatile不僅能保證可見性,還能防止重排序捉貌,這在多線程環(huán)境下非常重要支鸡。final也有一些規(guī)則來防止重排序,但是范圍沒有volatie那么寬趁窃,僅僅只針對(duì)部分場(chǎng)景牧挣,final的這個(gè)特性經(jīng)常被忽略。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末醒陆,一起剝皮案震驚了整個(gè)濱河市浸踩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌统求,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件据块,死亡現(xiàn)場(chǎng)離奇詭異码邻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)另假,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門像屋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人边篮,你說我怎么就攤上這事己莺∽喔Γ” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵凌受,是天一觀的道長(zhǎng)阵子。 經(jīng)常有香客問我,道長(zhǎng)胜蛉,這世上最難降的妖魔是什么挠进? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮誊册,結(jié)果婚禮上领突,老公的妹妹穿的比我還像新娘。我一直安慰自己案怯,他們只是感情好君旦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嘲碱,像睡著了一般金砍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悍汛,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天捞魁,我揣著相機(jī)與錄音,去河邊找鬼离咐。 笑死谱俭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宵蛀。 我是一名探鬼主播昆著,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼术陶!你這毒婦竟也來了凑懂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤梧宫,失蹤者是張志新(化名)和其女友劉穎接谨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塘匣,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脓豪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忌卤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扫夜。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出笤闯,到底是詐尸還是另有隱情堕阔,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布颗味,位于F島的核電站超陆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏脱衙。R本人自食惡果不足惜侥猬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捐韩。 院中可真熱鬧退唠,春花似錦、人聲如沸荤胁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仅政。三九已至垢油,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間圆丹,已是汗流浹背滩愁。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辫封,地道東北人硝枉。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像倦微,于是被迫代替她去往敵國(guó)和親妻味。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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