Java內(nèi)存模型

Java內(nèi)存模型

主內(nèi)存和工作內(nèi)存

Java虛擬機(jī)規(guī)范中定義了Java內(nèi)存模型(Java Memory Model,JMM),用于屏蔽各種硬件和操作系統(tǒng)之間的內(nèi)存訪問差異,以實(shí)現(xiàn)讓Java程序在各種平臺(tái)下都能達(dá)到一致的并發(fā)效果丙笋,JMM規(guī)范了Java虛擬機(jī)與計(jì)算機(jī)內(nèi)存是如何協(xié)同工作的:規(guī)定了一個(gè)線程如何和何時(shí)可以看到由其他線程修改過后的共享變量的值御板,以及在必須時(shí)如何同步的訪問共享變量怠肋。
原始的Java內(nèi)存模型存在一些不足笙各,因此Java內(nèi)存模型在Java1.5時(shí)被重新修訂杈抢。這個(gè)版本的Java內(nèi)存模型在Java8中仍然在使用春感。
Java內(nèi)存模型(不僅僅是JVM內(nèi)存分區(qū)):調(diào)用棧和本地變量存放在線程棧上虏缸,對(duì)象存放在堆上刽辙。Java內(nèi)存模型定義了線程與主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲(chǔ)在主內(nèi)存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存中存儲(chǔ)了該線程的讀/寫變量的副本宰缤。
Java的并發(fā)采用“共享內(nèi)存”模型朦乏,線程之間通過讀寫內(nèi)存的公共狀態(tài)進(jìn)行通訊氧骤。多個(gè)線程之間是不能通過直接傳遞數(shù)據(jù)交互的筹陵,它們之間交互只能通過共享變量實(shí)現(xiàn)朦佩。主要目的是定義程序中各個(gè)變量的訪問規(guī)則语稠。
JMM規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存(Main Memory)中。每個(gè)線程還有自己的工作內(nèi)存(Working Memory),線程的工作內(nèi)存中保存了該線程使用到的變量的主內(nèi)存的副本拷貝宙暇,線程對(duì)變量的所有操作(讀取占贫、賦值等)都必須在工作內(nèi)存中進(jìn)行型奥,而不能直接讀寫主內(nèi)存中的變量(volatile變量仍然有工作內(nèi)存的拷貝厢汹,但是由于它特殊的操作順序性規(guī)定烫葬,所以看起來如同直接在主內(nèi)存中讀寫訪問一般)凡蜻。不同的線程之間也無法直接訪問對(duì)方工作內(nèi)存中的變量划栓,線程之間值的傳遞都需要通過主內(nèi)存來完成忠荞。主內(nèi)存主要對(duì)應(yīng)Java堆中實(shí)例數(shù)據(jù)部分。工作內(nèi)存對(duì)應(yīng)于虛擬機(jī)棧中部分區(qū)域堂油。

棧和堆存放數(shù)據(jù)的不同:

  • 一個(gè)本地變量可能是原始類型府框,在這種情況下寓免,它總是“呆在”線程棧上。
  • 一個(gè)本地變量也可能是指向一個(gè)對(duì)象的一個(gè)引用撕予。在這種情況下实抡,引用(這個(gè)本地變量)存放在線程棧上吆寨,但是對(duì)象本身存放在堆上啄清。
  • 一個(gè)對(duì)象可能包含方法辣卒,這些方法可能包含本地變量荣茫。這些本地變量仍然存放在線程棧上场靴,即使這些方法所屬的對(duì)象存放在堆上旨剥。
  • 一個(gè)對(duì)象的成員變量可能隨著這個(gè)對(duì)象自身存放在堆上泞边。不管這個(gè)成員變量是原始類型還是引用類型疗杉。
  • 靜態(tài)成員變量跟隨著類定義一起也存放在堆上祈争。
  • 存放在堆上的對(duì)象可以被所有持有對(duì)這個(gè)對(duì)象引用的線程訪問秀仲。當(dāng)一個(gè)線程可以訪問一個(gè)對(duì)象時(shí)嗡午,它也可以訪問這個(gè)對(duì)象的成員變量。如果兩個(gè)線程同時(shí)調(diào)用同一個(gè)對(duì)象上的同一個(gè)方法荔睹,它們將會(huì)都訪問這個(gè)對(duì)象的成員變量僻他,但是每一個(gè)線程都擁有這個(gè)成員變量的私有拷貝。

Java線程之間的通訊由內(nèi)存模型JMM控制

  • JMM決定一個(gè)線程對(duì)變量的寫入何時(shí)對(duì)另一個(gè)線程可見满哪。
  • 線程之間共享變量存儲(chǔ)在主內(nèi)存中
  • 每個(gè)線程有一個(gè)私有的本地內(nèi)存哨鸭,里面存儲(chǔ)了讀/寫共享變量的副本像鸡。
  • JMM通過控制每個(gè)線程的本地內(nèi)存之間的交互哈恰,來為程序員提供內(nèi)存可見性保證蕊蝗。

內(nèi)存間的交互

  • lock(鎖定):作用于主內(nèi)存的變量蓬戚,把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占狀態(tài)。
  • unlock(解鎖):作用于主內(nèi)存的變量豫喧,把一個(gè)處于鎖定狀態(tài)的變量釋放出來紧显,釋放后的變量才可以被其他線程鎖定孵班。
  • read(讀取):作用于主內(nèi)存變量篙程,把主內(nèi)存的一個(gè)變量讀取到工作內(nèi)存中虱饿。
  • load(載入):作用于工作內(nèi)存,把read操作讀取到工作內(nèi)存的變量載入到工作內(nèi)存的變量副本中
  • use(使用):作用于工作內(nèi)存的變量渴肉,把工作內(nèi)存中的變量值傳遞給一個(gè)執(zhí)行引擎爽冕。
  • assign(賦值):作用于工作內(nèi)存的變量扇售。把執(zhí)行引擎接收到的值賦值給工作內(nèi)存的變量承冰。
  • store(存儲(chǔ)):把工作內(nèi)存的變量的值傳遞給主內(nèi)存
  • write(寫入):把store操作的值入到主內(nèi)存的變量中

注意在使用指令的時(shí)候需要滿足下面的規(guī)則:

  • 不允許read困乒、load、store迁霎、write操作之一單獨(dú)出現(xiàn)
  • 不允許一個(gè)線程丟棄assgin操作
  • 不允許一個(gè)線程不經(jīng)過assgin操作考廉,就把工作內(nèi)存中的值同步到主內(nèi)存中
  • 一個(gè)新的變量只能在主內(nèi)存中生成
  • 一個(gè)變量同一時(shí)刻只允許一條線程對(duì)其進(jìn)行l(wèi)ock操作昌粤。但lock操作可以被同一條線程執(zhí)行多次涮坐,只有執(zhí)行相同次數(shù)的unlock操作誓军,變量才會(huì)解鎖
  • 如果對(duì)一個(gè)變量進(jìn)行l(wèi)ock操作,將會(huì)清空工作內(nèi)存中此變量的值捷雕,在執(zhí)行引擎使用這個(gè)變量前,需要重新執(zhí)行l(wèi)oad或者assgin操作初始化變量的值。
  • 如果一個(gè)變量沒有被鎖定征绸,不允許對(duì)其執(zhí)行unlock操作管怠,也不允許unlock一個(gè)被其他線程鎖定的變量
  • 對(duì)一個(gè)變量執(zhí)行unlock操作之前渤弛,需要將該變量同步回主內(nèi)存中

三大特性:原子性她肯、可見性和有序性

原子性

一個(gè)操作不能被打斷晴氨,要么全部執(zhí)行完畢碉输,要么不執(zhí)行敷钾。在這點(diǎn)上有點(diǎn)類似于事務(wù)操作阻荒,要么全部執(zhí)行成功侨赡,要么回退到執(zhí)行該操作之前的狀態(tài)辆毡〔耙矗基本類型數(shù)據(jù)的訪問大都是原子操作眨攘。

1.多線程與原子性
解決問題的方式:使用同步代碼塊、Java內(nèi)存模型提供了lock和unlock操作來滿足這種需求共螺。虛擬機(jī)提供了字節(jié)碼指令monitorenter和monitorexist來隱式地使用這兩個(gè)操作藐不,這兩個(gè)字節(jié)碼指令反映到Java代碼中就是同步快——synchronized關(guān)鍵字雏蛮。

可見性

一個(gè)線程對(duì)共享變量做了修改之后,其他的線程立即能夠看到(感知到)該變量這種修改(變化)法梯。
Java內(nèi)存模型是通過將在工作內(nèi)存中的變量修改后的值同步到主內(nèi)存立哑,在讀取變量前從主內(nèi)存刷新最新值到工作內(nèi)存中刁憋,這種依賴主內(nèi)存的方式來實(shí)現(xiàn)可見性的至耻。

1.多線程讀同步與可見性
線程緩存導(dǎo)致的可見性問題:
如果兩個(gè)或者更多的線程在沒有正確的使用volatile聲明或者同步的情況下共享一個(gè)對(duì)象,在修改之后尘颓,CPU緩存沒有刷新到主內(nèi)存中,對(duì)象修改之后的版本對(duì)其他跑在其他CPU線程上的線程都是不可見的疤苹。這種方式導(dǎo)致每個(gè)線程擁有這個(gè)共享對(duì)象的私有拷貝,每個(gè)拷貝都停留在不同的CPU緩存中卧土。
解決方法:

  • 正確使用volatile關(guān)鍵字:Java內(nèi)存模型是通過在變量修改后將新值同步回主內(nèi)存尤莺,在變量讀取前從主內(nèi)存刷新變量值這種依賴主內(nèi)存作為傳遞媒介的方式來實(shí)現(xiàn)可見性的生棍。普通變量與volatile變量的區(qū)別是:volatile的特殊規(guī)則保證了新值能立即同步到主內(nèi)存颤霎,以及每個(gè)線程在每次使用volatile變量前都立即從主內(nèi)存刷新。
  • synchronized關(guān)鍵字:同步塊的可見性是由“如果對(duì)一個(gè)變量執(zhí)行l(wèi)ock操作,將會(huì)清空工作內(nèi)存中此變量的值友酱,在執(zhí)行引擎使用這個(gè)變量前需要重新執(zhí)行l(wèi)oad或assign操作初始化變量的值”晴音、“對(duì)一個(gè)變量執(zhí)行unlock操作之前,必須先把此變量同步回主內(nèi)存中(執(zhí)行store和write操作)”這兩條規(guī)則獲得的缔杉。
  • final關(guān)鍵字:被final修飾的字段在構(gòu)造器中一旦被初始化完成锤躁,并且構(gòu)造器沒有把“this”的引用傳遞出去(this引用逃逸是一件很危險(xiǎn)的事情,其他線程有可能通過這個(gè)引用訪問到“初始化了一半”的對(duì)象)或详,那么在其他線程就能看見final字段的值(無須同步)系羞。
  1. 指令沖排序?qū)е碌目梢娦詥栴}
    解決方法:可以使用volatile禁止指令重排序;使用synchronized來進(jìn)行加鎖保證互斥操作的順序

指令序列的排序分為:編譯器優(yōu)化的重排序;指令級(jí)優(yōu)化的重排序;內(nèi)存系統(tǒng)的重排序

有序性

在本線程內(nèi)觀察鸭叙,操作都是有序的勋乾;如果在一個(gè)線程中觀察另外一個(gè)線程,所有的操作都是無序的枝笨。前半句是指“線程內(nèi)表現(xiàn)為串行語義(WithIn Thread As-if-Serial Semantics)”,后半句是指“指令重排”現(xiàn)象和“工作內(nèi)存和主內(nèi)存同步延遲”現(xiàn)象徙融。
Java提供了兩個(gè)關(guān)鍵字volatile和synchronized來保證多線程之間操作的有序性,volatile關(guān)鍵字本身通過加入內(nèi)存屏障來禁止指令的重排序萨脑,而synchronized關(guān)鍵字通過一個(gè)變量在同一時(shí)間只允許有一個(gè)線程對(duì)其進(jìn)行加鎖的規(guī)則來實(shí)現(xiàn)。
在單線程程序中,不會(huì)發(fā)生“指令重排”和“工作內(nèi)存和主內(nèi)存同步延遲”現(xiàn)象胸哥,只在多線程程序中出現(xiàn)银酬。

happen-before原則

與程序員密切相關(guān)的happens-before規(guī)則如下:

  • 程序順序規(guī)則:一個(gè)線程中的每個(gè)操作宠哄,happens-before于該線程中任意的后續(xù)操作。
  • 監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖操作,happens-before于隨后對(duì)這個(gè)鎖的加鎖操作。
  • volatile域規(guī)則:對(duì)一個(gè)volatile域的寫操作,happens-before于任意線程后續(xù)對(duì)這個(gè)volatile域的讀淹遵。
  • 傳遞性規(guī)則:如果 A happens-before B辐真,且 B happens-before C,那么A happens-before C。

硬件內(nèi)存架構(gòu)

現(xiàn)代硬件內(nèi)存模型與Java內(nèi)存模型有一些不同,理解內(nèi)存模型架構(gòu)以及Java內(nèi)存模型如何與它協(xié)同工作也是非常重要的。

多CPU

一個(gè)現(xiàn)代計(jì)算機(jī)通常由兩個(gè)或者多個(gè)CPU齐遵。其中一些CPU還有多核想许。從這一點(diǎn)可以看出,在一個(gè)有兩個(gè)或者多個(gè)CPU的現(xiàn)代計(jì)算機(jī)上同時(shí)運(yùn)行多個(gè)線程是可能的。每個(gè)CPU在某一時(shí)刻運(yùn)行一個(gè)線程是沒有問題的。這意味著,如果你的Java程序是多線程的,在你的Java程序中每個(gè)CPU上一個(gè)線程可能同時(shí)(并發(fā))執(zhí)行。

CPU寄存器

每個(gè)CPU都包含一系列的寄存器,它們是CPU內(nèi)內(nèi)存的基礎(chǔ)。CPU在寄存器上執(zhí)行操作的速度遠(yuǎn)大于在主存上執(zhí)行的速度。這是因?yàn)镃PU訪問寄存器的速度遠(yuǎn)大于主存。

高速緩存cache

由于計(jì)算機(jī)的存儲(chǔ)設(shè)備與處理器的運(yùn)算速度之間有著幾個(gè)數(shù)量級(jí)的差距,所以現(xiàn)代計(jì)算機(jī)系統(tǒng)都不得不加入一層讀寫速度盡可能接近處理器運(yùn)算速度的高速緩存(Cache)來作為內(nèi)存與處理器之間的緩沖:將運(yùn)算需要使用到的數(shù)據(jù)復(fù)制到緩存中续扔,讓運(yùn)算能快速進(jìn)行善已,當(dāng)運(yùn)算結(jié)束后再從緩存同步回內(nèi)存之中洒扎,這樣處理器就無須等待緩慢的內(nèi)存讀寫了煌恢。CPU訪問緩存層的速度快于訪問主存的速度,但通常比訪問內(nèi)部寄存器的速度還要慢一點(diǎn)婿着。每個(gè)CPU可能有一個(gè)CPU緩存層挡篓,一些CPU還有多層緩存楼吃。在某一時(shí)刻炕置,一個(gè)或者多個(gè)緩存行(cache lines)可能被讀到緩存贩疙,一個(gè)或者多個(gè)緩存行可能再被刷新回主存癞尚。

內(nèi)存

一個(gè)計(jì)算機(jī)還包含一個(gè)主存胳徽。所有的CPU都可以訪問主存往核。主存通常比CPU中的緩存大得多蝶缀。

運(yùn)作原理

通常情況下,當(dāng)一個(gè)CPU需要讀取主存時(shí)税娜,它會(huì)將主存的部分讀到CPU緩存中弧岳。它甚至可能將緩存中的部分內(nèi)容讀到它的內(nèi)部寄存器中乐设,然后在寄存器中執(zhí)行操作嘹承。當(dāng)CPU需要將結(jié)果寫回到主存中去時(shí)窗价,它會(huì)將內(nèi)部寄存器的值刷新到緩存中,然后在某個(gè)時(shí)間點(diǎn)將值刷新回主存叹卷。

硬件內(nèi)存架構(gòu)帶來的問題

緩存一致性問題

在多處理器系統(tǒng)中撼港,每個(gè)處理器都有自己的高速緩存坪它,而它們又共享同一主內(nèi)存(MainMemory)〉勰担基于高速緩存的存儲(chǔ)交互很好地解決了處理器與內(nèi)存的速度矛盾往毡,但是也引入了新的問題:緩存一致性(CacheCoherence)。當(dāng)多個(gè)處理器的運(yùn)算任務(wù)都涉及同一塊主內(nèi)存區(qū)域時(shí)靶溜,將可能導(dǎo)致各自的緩存數(shù)據(jù)不一致的情況开瞭,如果真的發(fā)生這種情況,那同步回到主內(nèi)存時(shí)以誰的緩存數(shù)據(jù)為準(zhǔn)呢罩息?為了解決一致性的問題嗤详,需要各個(gè)處理器訪問緩存時(shí)都遵循一些協(xié)議,在讀寫時(shí)要根據(jù)協(xié)議來進(jìn)行操作瓷炮,這類協(xié)議有MSI葱色、MESI(IllinoisProtocol)、MOSI娘香、Synapse苍狰、Firefly及DragonProtocol等等。

指令重排序問題

為了使得處理器內(nèi)部的運(yùn)算單元能盡量被充分利用烘绽,處理器可能會(huì)對(duì)輸入代碼進(jìn)行亂序執(zhí)行(Out-Of-Order Execution)優(yōu)化舞痰,處理器會(huì)在計(jì)算之后將亂序執(zhí)行的結(jié)果重組,保證該結(jié)果與順序執(zhí)行的結(jié)果是一致的诀姚,但并不保證程序中各個(gè)語句計(jì)算的先后順序與輸入代碼中的順序一致响牛。因此,如果存在一個(gè)計(jì)算任務(wù)依賴另一個(gè)計(jì)算任務(wù)的中間結(jié)果赫段,那么其順序性并不能靠代碼的先后順序來保證呀打。與處理器的亂序執(zhí)行優(yōu)化類似,Java虛擬機(jī)的即時(shí)編譯器中也有類似的指令重排序(Instruction Reorder)優(yōu)化

Java內(nèi)存模型和硬件內(nèi)存架構(gòu)之間的橋接

Java內(nèi)存模型與硬件內(nèi)存架構(gòu)之間存在差異糯笙。硬件內(nèi)存架構(gòu)沒有區(qū)分線程棧和堆贬丛。對(duì)于硬件,所有的線程棧和堆都分布在主內(nèi)存中给涕。部分線程棧和堆可能有時(shí)候會(huì)出現(xiàn)在CPU緩存中和CPU內(nèi)部的寄存器中豺憔。

從抽象的角度來看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:

  • 線程之間的共享變量存儲(chǔ)在主內(nèi)存(Main Memory)中
  • 每個(gè)線程都有一個(gè)私有的本地內(nèi)存(Local Memory)够庙,本地內(nèi)存是JMM的一個(gè)抽象概念恭应,并不真實(shí)存在,它涵蓋了緩存耘眨、寫緩沖區(qū)昼榛、寄存器以及其他的硬件和編譯器優(yōu)化。本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的拷貝副本剔难。
  • 從更低的層次來說胆屿,主內(nèi)存就是硬件的內(nèi)存奥喻,而為了獲取更好的運(yùn)行速度,虛擬機(jī)及硬件系統(tǒng)可能會(huì)讓工作內(nèi)存優(yōu)先存儲(chǔ)于寄存器和高速緩存中非迹。
  • Java內(nèi)存模型中的線程的工作內(nèi)存(working memory)是cpu的寄存器和高速緩存的抽象描述环鲤。而JVM的靜態(tài)內(nèi)存儲(chǔ)模型(JVM內(nèi)存模型)只是一種對(duì)內(nèi)存的物理劃分而已,它只局限在內(nèi)存憎兽,而且只局限在JVM的內(nèi)存冷离。

參考文獻(xiàn)

Java內(nèi)存模型(JMM)總結(jié)
全面理解Java內(nèi)存模型
Java內(nèi)存模型和JVM內(nèi)存管理
JVM 完整深入解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市唇兑,隨后出現(xiàn)的幾起案子酒朵,更是在濱河造成了極大的恐慌,老刑警劉巖扎附,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔫耽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡留夜,警方通過查閱死者的電腦和手機(jī)匙铡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碍粥,“玉大人鳖眼,你說我怎么就攤上這事〗滥Γ” “怎么了钦讳?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長枕面。 經(jīng)常有香客問我愿卒,道長,這世上最難降的妖魔是什么潮秘? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任琼开,我火速辦了婚禮,結(jié)果婚禮上枕荞,老公的妹妹穿的比我還像新娘柜候。我一直安慰自己,他們只是感情好躏精,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布渣刷。 她就那樣靜靜地躺著,像睡著了一般玉控。 火紅的嫁衣襯著肌膚如雪飞主。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天高诺,我揣著相機(jī)與錄音碌识,去河邊找鬼。 笑死虱而,一個(gè)胖子當(dāng)著我的面吹牛筏餐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牡拇,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼魁瞪,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了惠呼?” 一聲冷哼從身側(cè)響起导俘,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剔蹋,沒想到半個(gè)月后旅薄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泣崩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年少梁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矫付。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凯沪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出买优,到底是詐尸還是另有隱情妨马,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布杀赢,位于F島的核電站烘跺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏葵陵。R本人自食惡果不足惜液荸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脱篙。 院中可真熱鬧娇钱,春花似錦、人聲如沸绊困。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秤朗。三九已至煤蹭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背硝皂。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工常挚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稽物。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓奄毡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贝或。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吼过,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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