☆啃碎并發(fā)(10):內(nèi)存模型之內(nèi)部原理

0 前言

如上一篇文章所述刨晴,Java內(nèi)存模型規(guī)范了Java虛擬機與計算機內(nèi)存是如何協(xié)同工作的稠炬。Java虛擬機是一個完整計算機的模型陶缺,因此点额,這個模型自然會包含一個內(nèi)存模型—又稱為Java內(nèi)存模型舔株。

如果你想設計表現(xiàn)良好的并發(fā)程序,理解Java內(nèi)存模型是非常重要的还棱。Java內(nèi)存模型規(guī)定了如何和何時可以看到由其他線程修改過后的共享變量的值载慈,以及在必須時如何同步的訪問共享變量

1 Java內(nèi)存模型

我們先來看看Java 線程運行內(nèi)存示意圖珍手,如下圖所示:

Java 線程運行內(nèi)存示意圖

這張圖告訴我們在線程運行的時候有一個內(nèi)存專用的一小塊內(nèi)存办铡,當Java程序會將變量同步到線程所在的內(nèi)存辞做,這時候會操作工作內(nèi)存中的變量,而線程中變量的值何時同步回主內(nèi)存是不可預期的寡具。

因此秤茅,依據(jù)上面圖的線程運行內(nèi)存示意圖,Java內(nèi)存模型在JVM內(nèi)部抽象劃分為線程棧和堆童叠。如下圖所示:

JMM劃分為線程棧和堆

1.1 線程棧與堆

每一個運行在Java虛擬機里的線程都擁有自己的線程棧框喳。這個線程棧包含了 線程調(diào)用的方法當前執(zhí)行點相關的信息,同時線程棧具有如下特性:

  1. 一個線程僅能訪問自己的線程棧厦坛;
  2. 一個線程創(chuàng)建的本地變量對其它線程不可見五垮,僅自己可見;

即使兩個線程執(zhí)行同樣的代碼杜秸,這兩個線程任然在在自己的線程棧中的代碼來創(chuàng)建本地變量放仗。因此,每個線程擁有每個本地變量的獨有版本亩歹。

所有原始類型的本地變量都存放在線程棧上匙监,因此對其它線程不可見。一個線程可能向另一個線程傳遞一個原始類型變量的拷貝小作,但是它不能共享這個原始類型變量自身亭姥。

堆上包含在Java程序中創(chuàng)建的所有對象,無論是哪一個對象創(chuàng)建的顾稀。這包括原始類型的對象版本达罗。如果一個對象被創(chuàng)建然后賦值給一個局部變量,或者用來作為另一個對象的成員變量静秆,這個對象任然是存放在堆上粮揉。

所以,調(diào)用棧和本地變量存放在線程棧上抚笔,對象存放在堆上扶认,如下圖所示:

線程棧與堆 & 變量、對象殊橙、調(diào)用棧
  1. 一個本地變量可能是原始類型辐宾,在這種情況下,它總是“呆在”線程棧上膨蛮;
  2. 一個本地變量也可能是指向一個對象的一個引用叠纹。在這種情況下,引用(這個本地變量)存放在線程棧上敞葛,但是對象本身存放在堆上誉察;
  3. 一個對象可能包含方法,這些方法可能包含本地變量惹谐。這些本地變量任然存放在線程棧上持偏,即使這些方法所屬的對象存放在堆上驼卖。
  4. 一個對象的成員變量可能隨著這個對象自身存放在堆上。不管這個成員變量是原始類型還是引用類型综液。
  5. 靜態(tài)成員變量跟隨著類定義一起也存放在堆上款慨。

存放在堆上的對象可以被所有持有對這個對象引用的線程訪問。當一個線程可以訪問一個對象時谬莹,它也可以訪問這個對象的成員變量。如果兩個線程同時調(diào)用同一個對象上的同一個方法桩了,它們將會都訪問這個對象的成員變量附帽,但是每一個線程都擁有這個本地變量的私有拷貝。

上面說到的幾點井誉,如下圖所示:

棧蕉扮、堆 & 本地變量、靜態(tài)變量

1.2 CPU與內(nèi)存

眾所周知颗圣,CPU是計算機的大腦喳钟,它負責執(zhí)行程序的指令。內(nèi)存負責存數(shù)據(jù)在岂,包括程序自身數(shù)據(jù)奔则。同樣大家都知道,內(nèi)存比CPU慢很多蔽午,現(xiàn)在獲取內(nèi)存中的一條數(shù)據(jù)大概需要200多個CPU周期(CPU cycles)易茬,而CPU寄存器一般情況下1個CPU周期就夠了。下面是CPU Cache的簡單示意圖:

CPU Cache示意圖

隨著多核的發(fā)展及老,CPU Cache分成了三個級別:L1抽莱,L2,L3骄恶。級別越小越接近CPU食铐,所以速度也更快,同時也代表著容量越小僧鲁。

  1. L1是最接近CPU的虐呻,它容量最小,例如32K悔捶,速度最快铃慷,每個核上都有一個L1 Cache(準確地說每個核上有兩個L1 Cache, 一個存數(shù)據(jù) L1d Cache, 一個存指令 L1i Cache)。
  2. L2 Cache 更大一些蜕该,例如256K犁柜,速度要慢一些,一般情況下每個核上都有一個獨立的L2 Cache堂淡。
  3. L3 Cache是三級緩存中最大的一級馋缅,例如12MB扒腕,同時也是最慢的一級, 在同一個CPU插槽之間的核共享一個L3 Cache。
從CPU到 大約需要的CPU周期 大約需要的時間(單位ns)
寄存器 1 cycle
L1 Cache ~3-4 cycles ~0.5-1 ns
L2 Cache ~10-20 cycles ~3-7 ns
L3 Cache ~40-45 cycles ~15 ns
跨槽傳輸 ~20 ns
內(nèi)存 ~120-240 cycles ~60-120ns

在Linux下面用 cat /proc/cpuinfo萤悴,或Ubuntu下 lscpu 看看自己機器的緩存情況瘾腰,更細的可以通過以下命令看看:

$ cat /sys/devices/system/cpu/cpu0/cache/index0/size
32K
$ cat /sys/devices/system/cpu/cpu0/cache/index0/type
Data
$ cat /sys/devices/system/cpu/cpu0/cache/index0/level
1
$ cat /sys/devices/system/cpu/cpu3/cache/index3/level
3

就像數(shù)據(jù)庫cache一樣覆履,獲取數(shù)據(jù)時首先會在最快的cache中找數(shù)據(jù)蹋盆,如果沒有命中(Cache miss) 則往下一級找,直到三層Cache都找不到,那只要向內(nèi)存要數(shù)據(jù)了硝全。一次次地未命中栖雾,代表獲取數(shù)據(jù)消耗的時間越長。

同時伟众,為了高效地存取緩存析藕,不是簡單隨意地將單條數(shù)據(jù)寫入緩存的。緩存是由緩存行組成的凳厢,典型的一行是64字節(jié)账胧。可以通過下面的shell命令先紫,查看 cherency_line_size 就知道知道機器的緩存行是多大:

$ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
64

CPU存取緩存都是以“行”為最小單位操作的治泥。比如:一個Java long型占8字節(jié),所以從一條緩存行上你可以獲取到8個long型變量泡孩。所以如果你訪問一個long型數(shù)組车摄,當有一個long被加載到cache中, 你將無消耗地加載了另外7個。所以你可以非陈嘏福快地遍歷數(shù)組吮播。

2 緩存一致性

由于CPU和主存的處理速度上存在一定差別,為了匹配這種差距眼俊,提升計算機能力意狠,人們在CPU和主存之間增加了多層高速緩存。每個CPU會有L1疮胖、L2甚至L3緩存环戈,在多核計算機中會有多個CPU,那么就會存在多套緩存澎灸,在這多套緩存之間的數(shù)據(jù)就可能出現(xiàn)不一致的現(xiàn)象院塞。為了解決這個問題,有了內(nèi)存模型性昭。內(nèi)存模型定義了共享內(nèi)存系統(tǒng)中多線程程序讀寫操作行為的規(guī)范拦止。通過這些規(guī)則來規(guī)范對內(nèi)存的讀寫操作,從而保證指令執(zhí)行的正確性。

其實Java內(nèi)存模型告訴我們通過使用關鍵詞 “synchronized”“volatile” 可以讓Java保證某些約束:

  1. “volatile” — 保證讀寫的都是主內(nèi)存的變量汹族。
  2. “synchronized” — 保證在塊開始時都同步主內(nèi)存的值到工作內(nèi)存萧求,而塊結束時將變量同步回主內(nèi)存。

通過以上描述我們就可以寫出線程安全的Java程序顶瞒,JDK也同時幫我們屏蔽了很多底層的東西夸政。

所以,在編譯器各種優(yōu)化及多種類型的微架構平臺上榴徐,Java語言規(guī)范制定者試圖創(chuàng)建一個虛擬的概念并傳遞到Java程序員守问,讓他們能夠在這個虛擬的概念上寫出線程安全的程序來,而編譯器實現(xiàn)者會根據(jù)Java語言規(guī)范中的各種約束在不同的平臺上達到Java程序員所需要的線程安全這個目的坑资。

那么酪碘,在多種類型微架構平臺上,又是如何解決緩存不一致性問題的呢盐茎?這是眾多CPU廠商必須解決的問題。為了解決前面提到的緩存數(shù)據(jù)不一致的問題徙赢,人們提出過很多方案字柠,通常來說有以下2種方案:

  1. 通過在總線加LOCK#鎖的方式;
  2. 通過緩存一致性協(xié)議(Cache Coherence Protocol)狡赐;

2.1 總線的概念

首先窑业,上面的兩種方案,其實都涉及到了總線的概念枕屉,那到底什么是總線呢常柄?總線是處理器與主存以及處理器與處理器之間進行通信的媒介,有兩種基本的互聯(lián)結構:SMP(symmetric multiprocessing 對稱多處理)和NUMA(nonuniform memory access 非一致內(nèi)存訪問)搀擂。

SMP(對稱多處理)和NUMA(非一致內(nèi)存訪問)

SMP系統(tǒng)結構非常普通西潘,因為它們最容易構建,很多小型服務器采用這種結構哨颂。處理器和存儲器之間采用總線互聯(lián)喷市,處理器和存儲器都有負責發(fā)送和監(jiān)聽總線廣播的信息的總線控制單元。但是同一時刻只能有一個處理器(或存儲控制器)在總線上廣播威恼,所有的處理器都可以監(jiān)聽品姓。很容易看出,對總線的使用是SMP結構的瓶頸箫措。

NUMP系統(tǒng)結構中腹备,一系列節(jié)點通過點對點網(wǎng)絡互聯(lián),像一個小型互聯(lián)網(wǎng)斤蔓,每個節(jié)點包含一個或多個處理器和一個本地存儲器植酥。一個節(jié)點的本地存儲對于其他節(jié)點是可見的,所有節(jié)點的本地存儲一起形成了一個可以被所有處理器共享的全局存儲器附迷。可以看出惧互,NUMP的本地存儲是共享的哎媚,而不是私有的,這點和SMP是不同的喊儡。NUMP的問題是網(wǎng)絡比總線復制拨与,需要更加復雜的協(xié)議,處理器訪問自己節(jié)點的存儲器速度快于訪問其他節(jié)點的存儲器艾猜。NUMP的擴展性很好买喧,所以目前很多大中型的服務器在采用NUMP結構

對于上層程序員來說匆赃,最需要理解的是總線線是一種重要的資源淤毛,使用的好壞會直接影響程序的執(zhí)行性能

2.2 總線加Lock

在早期的CPU當中算柳,是可以通過在總線上加LOCK#鎖的形式來解決緩存不一致的問題低淡。因為CPU和其他部件進行通信都是通過總線來進行的,如果對總線加LOCK#鎖的話瞬项,也就是說阻塞了其他CPU對其他部件訪問(如內(nèi)存)蔗蹋,從而使得只能有一個CPU能使用這個變量的內(nèi)存。在總線上發(fā)出了LCOK#鎖的信號囱淋,那么只有等待這段代碼完全執(zhí)行完畢之后猪杭,其他CPU才能從其內(nèi)存讀取變量,然后進行相應的操作妥衣。這樣就解決了緩存不一致的問題皂吮。

但是由于在鎖住總線期間,其他CPU無法訪問內(nèi)存税手,會導致效率低下蜂筹。因此出現(xiàn)了第二種解決方案,通過緩存一致性協(xié)議來解決緩存一致性問題冈止。

2.3 緩存一致性協(xié)議

一致性要求是指狂票,若cache中某個字段被修改,那么在主存(以及更高層次)上熙暴,該字段的副本必須立即或最后加以修改闺属,并確保它者引用主存上該字內(nèi)容的正確性。
  
當代多處理器系統(tǒng)中周霉,每個處理器大都有自己的cache掂器。同一主存塊的拷貝能同時存于不同cache中,若允許處理器各自獨立地修改自己的cache俱箱,就會出現(xiàn)不一致問題国瓮。解決此問題有軟件辦法和硬件辦法。硬件辦法能動態(tài)地識別出不一致產(chǎn)生的條件并予以及時處理,從而使cache的使用有很高的效率乃摹。并且此辦法對程序員和系統(tǒng)軟件開發(fā)人員是透明的禁漓,減輕了軟件研制負擔,從而普遍被采用孵睬。

軟件辦法最出名的就是Intel 的MESI協(xié)議播歼,MESI協(xié)議保證了每個緩存中使用的共享變量的副本是一致的。MESI協(xié)議是一種采用寫--無效方式的監(jiān)聽協(xié)議掰读。它要求每個cache行有兩個狀態(tài)位秘狞,用于描述該行當前是處于修改態(tài)(M)、專有態(tài)(E)蹈集、共享態(tài)(S)或者無效態(tài)(I)中的哪種狀態(tài)烁试,從而決定它的讀/寫操作行為。這四種狀態(tài)的定義是:

  1. 修改態(tài)(Modified):這行數(shù)據(jù)有效拢肆,數(shù)據(jù)被修改了减响,和內(nèi)存中的數(shù)據(jù)不一致,數(shù)據(jù)只存在于本Cache中郭怪。
  2. 專有態(tài)(Exclusive):這行數(shù)據(jù)有效辩蛋,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)只存在于本Cache中移盆。
  3. 共享態(tài)(Shared):這行數(shù)據(jù)有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致伤为,數(shù)據(jù)存在于很多Cache中咒循。
  4. 無效態(tài)(Invalid):這行數(shù)據(jù)無效。

MESI協(xié)議適合以總線為互連機構的多處理器系統(tǒng)绞愚。各cache控制器除負責響應自己CPU的內(nèi)存讀寫操作(包括讀/寫命中與未命中)外叙甸,還要負責監(jiān)聽總線上的其它CPU的內(nèi)存讀寫活動(包括讀監(jiān)聽命中與寫監(jiān)聽命中)并對自己的cache予以相應處理所有這些處理過程要維護cache一致性位衩,必須符合MESI協(xié)議狀態(tài)轉(zhuǎn)換規(guī)則裆蒸。

MESI的總線監(jiān)聽與狀態(tài)轉(zhuǎn)換

下面由圖的四個頂點出發(fā),介紹總線監(jiān)聽與狀態(tài)轉(zhuǎn)換規(guī)則:

  1. 該I無效行在 自身Cache讀未命中 將被相應內(nèi)存塊填充以建立新行時糖驴,讀監(jiān)聽命中僚祷,說明其它Cache正在讀同地址的內(nèi)存塊,以建立新行贮缕。故為多Cache共享行辙谜,應為S狀態(tài),并應繼續(xù)發(fā)出讀監(jiān)聽廣播感昼,使其它Cache的類似情況效仿装哆。
  2. 該I無效行在 自身Cache讀未命中 將被相應內(nèi)存塊填充以建立新行時,未有讀監(jiān)聽命中,為本Cache專有蜕琴,故新建行應為E狀態(tài)萍桌。
  3. 該I無效行在 自身Cache寫未命中 時,將先讀入相應內(nèi)存塊填充新行后凌简,再進行寫修改上炎,與原內(nèi)存正本的數(shù)據(jù)不一致,故新建行為M狀態(tài)号醉。
  4. 該S共享行 寫監(jiān)聽命中反症,說明別的Cache由于寫命中修改了同此地址的行,根據(jù)寫無效原則畔派,此共享行應改變?yōu)闊o效(I)狀態(tài)铅碍。
  5. 該S共享行 讀命中,狀態(tài)不變线椰。
  6. 該S共享行 讀監(jiān)聽命中胞谈,說明其它Cache正在讀同地址內(nèi)存塊,以建立新行憨愉,此時該共享行狀態(tài)不必改變烦绳,但應繼續(xù)發(fā)讀監(jiān)聽廣播,供它者監(jiān)聽配紫。
  7. 該S共享行 寫命中径密,其中某字被改寫,與內(nèi)存正本不一致躺孝,故應改為M狀態(tài)享扔,且應發(fā)出共享行寫命中監(jiān)聽廣播,使其它Cache同地址行作廢(同 4)植袍。
  8. 該E專有行 讀監(jiān)聽命中惧眠,說明別的Cache正在讀同地址的內(nèi)存正本,以建立新行于个,故其狀態(tài)應改為S狀態(tài)氛魁,并發(fā)出讀監(jiān)聽廣播,以使同此情況及1效仿之厅篓。
  9. 該E專有行 讀命中 不必改變狀態(tài)秀存。
  10. 該E專有行 寫監(jiān)聽命中,說明別的Cache由于寫未命中而訪問同地址的內(nèi)存正本羽氮,該E態(tài)行內(nèi)容即將過時应又,故應作廢。
  11. 該E專有行 寫命中乏苦,只改變狀態(tài)為M態(tài)即可株扛,無須他者監(jiān)聽尤筐。
  12. 該M修改行 寫命中 狀態(tài)不變。
  13. 該M修改行 讀命中 狀態(tài)不變洞就。
  14. 該M修改行 讀監(jiān)聽命中盆繁,應將該行最新數(shù)據(jù)寫回內(nèi)存正本后變?yōu)镾狀態(tài)。并發(fā)出讀監(jiān)聽廣播旬蟋,供他者監(jiān)聽油昂。
  15. 該M修改行 寫監(jiān)聽命中,說明別的Cache由于寫未命中而訪問了同地址的內(nèi)存塊(同3)倾贰,將實行先讀后修改冕碟,此時本地M修改行應搶先寫回主存,然后作廢匆浙,以保證別的Cache讀出整行而未被修改數(shù)據(jù)的正確性安寺。
  16. 該M修改行 整行寫監(jiān)聽命中,說明別的Cache由于寫未命中而訪問了同地址的內(nèi)存塊首尼,將實行先讀后整行的修改挑庶,此時本地M修改行不必寫回主存,只作廢即可软能。

上述分析可以看出迎捺,雖然各cache控制器隨時都在監(jiān)聽系統(tǒng)總線,但能監(jiān)聽到的只有讀未命中查排、寫未命中以及共享行寫命中三種情況凳枝。總線監(jiān)控邏輯并不復雜跋核,增添的系統(tǒng)總線傳輸開銷也不大范舀,MESI協(xié)議卻有力地保證了主存塊臟拷貝在多cache中的唯一性,并能及時寫回了罪,保證cache主存存取的正確性。

但是聪全,值得注意的是泊藕,傳統(tǒng)的MESI協(xié)議中有兩個行為的執(zhí)行成本比較大幌陕。一個是將某個Cache Line標記為Invalid狀態(tài)霹崎,另一個是當某Cache Line當前狀態(tài)為Invalid時寫入新的數(shù)據(jù)富雅。所以CPU通過Store Buffer和Invalidate Queue組件來降低這類操作的延時释涛。如下圖所示:

CPU通過Store Buffer和Invalidate Queue組件來降低這類操作的延時
  1. 當一個CPU進行寫入時备埃,首先會給其它CPU發(fā)送Invalid消息慧域,然后把當前寫入的數(shù)據(jù)寫入到Store Buffer中帽馋,然后異步在某個時刻真正的寫入到Cache中睡扬。
  2. 當前CPU核如果要讀Cache中的數(shù)據(jù)谦炬,需要先掃描Store Buffer之后再讀取Cache悦屏。
  3. 但是此時其它CPU核是看不到當前核的Store Buffer中的數(shù)據(jù)的节沦,要等到Store Buffer中的數(shù)據(jù)被刷到了Cache之后才會觸發(fā)失效操作
  4. 而當一個CPU核收到Invalid消息時础爬,會把消息寫入自身的Invalidate Queue中甫贯,隨后異步將其設為Invalid狀態(tài)
  5. 和Store Buffer不同的是看蚜,當前CPU核心使用Cache時并不掃描Invalidate Queue部分叫搁,所以可能會有極短時間的臟讀問題

所以供炎,MESI協(xié)議渴逻,可以保證緩存的一致性,但是無法保證實時性音诫,可能會有極短時間的臟讀問題惨奕。

其實,并非所有情況都會使用緩存一致性的纽竣,如:被操作的數(shù)據(jù)不能被緩存在CPU內(nèi)部或操作數(shù)據(jù)跨越多個緩存行(狀態(tài)無法標識)墓贿,則處理器會調(diào)用總線鎖定;另外當CPU不支持緩存鎖定時蜓氨,自然也只能用總線鎖定了聋袋,比如說奔騰486以及更老的CPU。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末穴吹,一起剝皮案震驚了整個濱河市幽勒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌港令,老刑警劉巖啥容,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異顷霹,居然都是意外死亡咪惠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門淋淀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遥昧,“玉大人,你說我怎么就攤上這事朵纷√砍簦” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵袍辞,是天一觀的道長鞋仍。 經(jīng)常有香客問我,道長搅吁,這世上最難降的妖魔是什么威创? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任落午,我火速辦了婚禮,結果婚禮上那婉,老公的妹妹穿的比我還像新娘板甘。我一直安慰自己,他們只是感情好详炬,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布盐类。 她就那樣靜靜地躺著,像睡著了一般呛谜。 火紅的嫁衣襯著肌膚如雪在跳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天隐岛,我揣著相機與錄音猫妙,去河邊找鬼。 笑死聚凹,一個胖子當著我的面吹牛割坠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妒牙,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼彼哼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了湘今?” 一聲冷哼從身側響起敢朱,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎摩瞎,沒想到半個月后拴签,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡旗们,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年蚓哩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片上渴。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡岸梨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驰贷,到底是詐尸還是另有隱情,我是刑警寧澤洛巢,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布括袒,位于F島的核電站,受9級特大地震影響稿茉,放射性物質(zhì)發(fā)生泄漏锹锰。R本人自食惡果不足惜芥炭,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恃慧。 院中可真熱鬧园蝠,春花似錦、人聲如沸痢士。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怠蹂。三九已至善延,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間城侧,已是汗流浹背易遣。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嫌佑,地道東北人豆茫。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像屋摇,于是被迫代替她去往敵國和親揩魂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

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