12. 多線程

寫在之前

以下是《瘋狂Java講義》中的一些知識趣效,如有錯誤,煩請指正猪贪。

概述

** 進程特征**

  • 獨立性:進程是系統(tǒng)中獨立存在的實體跷敬,它可以擁有自己獨立的資源,每一個進程都擁有自己私有的地址空間热押。在沒有經(jīng)過進程本身允許的情況下西傀,一個用戶進程不可以直接訪問其他進程的地址空間。
  • 動態(tài)性:進程與程序的區(qū)別在于:程序只是一個靜態(tài)的指令集合桶癣,而進程是一個正在系統(tǒng)中活動的指令集合拥褂。在進程中加入了時間的概念。進程具有自己的生命周期和各種不同的狀態(tài)牙寞,這些概念在程序中都是不具備的饺鹃。
  • 并發(fā)性:多個進程可以在單個處理器上并發(fā)執(zhí)行,多個進程之間不會互相影響碎税。

并發(fā)與并行
并行指在同一時刻尤慰,有多條指令在多個處理器上同時執(zhí)行馏锡;并發(fā)指在同一時刻只能有一條指令執(zhí)行雷蹂,但多個進程指令被快速輪換執(zhí)行,使得宏觀上有多個進程同時執(zhí)行的效果杯道。

多線程編程的優(yōu)勢

  • 進程間不能共享內(nèi)存匪煌,但線程之間共享內(nèi)存非常容易。
  • 系統(tǒng)創(chuàng)建進程需要為該進程重新分配系統(tǒng)資源党巾,但創(chuàng)建線程則代價小得多萎庭,因此使用多線程來實現(xiàn)多任務(wù)并發(fā)比多進程的效率高。
  • Java語言內(nèi)置的多線程功能支持齿拂,而不是單純地作為底層操作系統(tǒng)的調(diào)度方式驳规,從而簡化了Java的多線程編程

創(chuàng)建線程

**a. 繼承Thread類創(chuàng)建線程類 **

  1. 定義Thread類的子類,并重寫該類的run方法署海,該run方法的方法體就是代表了線程需要完成的任務(wù)吗购。因此,我們經(jīng)常把run方法稱為線程執(zhí)行體砸狞。
  2. 創(chuàng)建Thread子類的實例捻勉,即創(chuàng)建了線程對象。
  3. 調(diào)用線程對象的start方法來啟動該線程刀森。
public class FirstThread extends Thread
{
    private int i ;
    // 重寫run方法踱启,run方法的方法體就是線程執(zhí)行體
    public void run()
    {
        for ( ; i < 100 ; i++ )
        {
            // 當(dāng)線程類繼承Thread類時,直接使用this即可獲取當(dāng)前線程
            // Thread對象的getName()返回當(dāng)前該線程的名字
            // 因此可以直接調(diào)用getName()方法返回當(dāng)前線程的名
            System.out.println(getName() +  " " + i);
        }
    }
    public static void main(String[] args)
    {
        for (int i = 0; i < 100;  i++)
        {
            // 調(diào)用Thread的currentThread方法獲取當(dāng)前線程
            System.out.println(Thread.currentThread().getName()
                +  " " + i);
            if (i == 20)
            {
                // 創(chuàng)建、并啟動第一條線程
                new FirstThread().start();
                // 創(chuàng)建埠偿、并啟動第二條線程
                new FirstThread().start();
            }
        }
    }
}

**b. 實現(xiàn)Runnable接口創(chuàng)建線程類 **

  1. 定義Runnable接口的實現(xiàn)類透罢,并重寫該接口的run方法,該run方法的方法體同樣是該線程的線程執(zhí)行體胚想。
  2. 創(chuàng)建Runnable實現(xiàn)類的實例琐凭,并以此實例作為Thread的target來創(chuàng)建Thread對象,該Thread對象才是真正的線程對象浊服。
  3. 調(diào)用線程對象的start方法來啟動該線程统屈。
public class SecondThread implements Runnable
{
    private int i ;
    // run方法同樣是線程執(zhí)行體
    public void run()
    {
        for ( ; i < 100 ; i++ )
        {
            // 當(dāng)線程類實現(xiàn)Runnable接口時,
            // 如果想獲取當(dāng)前線程牙躺,只能用Thread.currentThread()方法愁憔。
            System.out.println(Thread.currentThread().getName()
                + "  " + i);
        }
    }

    public static void main(String[] args)
    {
        for (int i = 0; i < 100;  i++)
        {
            System.out.println(Thread.currentThread().getName()
                + "  " + i);
            if (i == 20)
            {
                SecondThread st = new SecondThread();     // ①
                // 通過new Thread(target , name)方法創(chuàng)建新線程
                new Thread(st , "新線程1").start();
                new Thread(st , "新線程2").start();
            }
        }
    }
}

c. 實現(xiàn)Callable創(chuàng)建多線程

Callable接口,該接口怎么看都像是Runnable接口的增強版孽拷,Callable接口也提供了一個call()方法可以作為線程執(zhí)行體吨掌,但call方法比run()方法功能更強大:call()方法可以有返回值;call()可以聲明拋出異常 脓恕。
Callable接口有泛型限制膜宋,其接口里的泛型形參類型與call方法返回值類型相同。而且Callable接口是函數(shù)式接口炼幔,因此可使用Lambda表達(dá)式創(chuàng)建Callable對象

  1. 定義Callable接口的實現(xiàn)類秋茫,并重寫該接口的call方法,該call方法的方法體同樣是該線程的線程執(zhí)行體乃秀。
  2. 創(chuàng)建Callable實現(xiàn)類的實例肛著,并將該實例包裝成FutureTask,F(xiàn)utureTask實現(xiàn)了Runnable接口跺讯。
  3. 將FutureTask實例作為Thread的target來創(chuàng)建Thread對象枢贿,該Thread對象才是真正的線程對象。
  4. 調(diào)用線程對象的start方法來啟動該線程刀脏。
public class ThirdThread
{
    public static void main(String[] args)
    {
        // 創(chuàng)建Callable對象
        ThirdThread rt = new ThirdThread();
        // 先使用Lambda表達(dá)式創(chuàng)建Callable<Integer>對象局荚,無須先創(chuàng)建Callable實現(xiàn)類
        // 使用FutureTask來包裝Callable對象
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)() -> {
            int i = 0;
            for ( ; i < 100 ; i++ )
            {
                System.out.println(Thread.currentThread().getName()
                    + " 的循環(huán)變量i的值:" + i);
            }
            // call()方法可以有返回值
            return i;
        });
        for (int i = 0 ; i < 100 ; i++)
        {
            System.out.println(Thread.currentThread().getName()
                + " 的循環(huán)變量i的值:" + i);
            if (i == 20)
            {
                // 實質(zhì)還是以Callable對象來創(chuàng)建、并啟動線程
                new Thread(task , "有返回值的線程").start();
            }
        }
        try
        {
            // 獲取線程返回值
            System.out.println("子線程的返回值:" + task.get());
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
}


兩種線程方式的對比
采用實現(xiàn)Runnable接口方式的多線程:

  • 線程類只是實現(xiàn)了Runnable接口愈污,還可以可以繼承其他類耀态。在這種方式下,可以多個線程共享同一個target對象钙畔,所以非常適合多個相同線程來處理
  • 同一份資源的情況茫陆,從而可以將CPU,代碼和數(shù)據(jù)分開擎析,形成清晰的模型簿盅,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?/li>
  • 劣勢是:編程稍稍復(fù)雜挥下,如果需要訪問當(dāng)前線程,必須使用Thread.currentThread()方法桨醋。

采用繼承Thread類方式的多線程:

  • 劣勢是:因為線程類已經(jīng)繼承了Thread類棚瘟,所以不能再繼承其他父類。
  • 優(yōu)勢是:編寫簡單喜最,如果需要訪問當(dāng)前線程偎蘸,無需使用Thread.currentThread()方法,直接使用this即可獲得當(dāng)前線程瞬内。

綜上一般推薦采用實現(xiàn)Runnable接口的方式來創(chuàng)建多進程迷雪。

線程的生命周期

線程狀態(tài)轉(zhuǎn)換

線程死亡
線程會以以下三種方式之一結(jié)束,一個結(jié)束后就處于死亡狀態(tài):

  1. run()方法執(zhí)行完成虫蝶,線程正常結(jié)束章咧。
  2. 線程拋出一個未捕獲的 Exception或Error。
  3. 直接調(diào)用該線程的stop()方法來結(jié)束該線程——該方法容易導(dǎo)致死鎖能真,通常不推薦使用

join線程
Thread提供了讓一個線程等待另一個線程完成的方法:join() 方法赁严。當(dāng)在某個程序執(zhí)行流中調(diào)用其他線程的join()方法時,調(diào)用線程將被阻塞粉铐,直到被join方法加入的join線程完成為止疼约。
join()方法通常由使用線程的程序調(diào)用,以將大問題劃分成許多小問題蝙泼,每個小問題分配一個線程程剥。當(dāng)所有的小問題都得到處理后,再調(diào)用主線程來進一步操作踱承。

后臺線程

有一種線程倡缠,它是在后臺運行的哨免,它的任務(wù)是為其他的線程提供服務(wù)茎活,這種線程被稱為“后臺線程(Daemon Thread)”,又稱為“守護線程” 或“精靈線程”琢唾。JVM的垃圾回收線程就是典型的后臺線程载荔。
后臺線程有個特征:如果所有的前臺線程都死亡常空,后臺線程會自動死亡掀泳。
調(diào)用Thread對象setDaemon(true)方法可將指定線程設(shè)置成后臺線程。

線程睡眠
如果我們需要讓當(dāng)前正在執(zhí)行的線程暫停一段時間啤它,并進入阻塞狀態(tài)普办,則可以通過調(diào)用Thread類的靜態(tài)sleep方法工扎,sleep方法有兩種重載的形式:
static void sleep(long millis):讓當(dāng)前正在執(zhí)行的線程暫停millis毫秒,并進入阻塞狀態(tài)衔蹲,該方法受到系統(tǒng)計時器和線程調(diào)度器的精度和準(zhǔn)確度的影響肢娘。
static void sleep(long millis, int nanos):讓當(dāng)前正在執(zhí)行的線程暫停millis毫秒加nanos毫微妙,并進入阻塞狀態(tài),該方法受到系統(tǒng)計時器和線程調(diào)度器的精度和準(zhǔn)確度的影響橱健。

線程讓步
yield()方法是一個和sleep方法有點相似的方法而钞,它也是一個Thread類提供的一個靜態(tài)方法,它也可以讓當(dāng)前正在執(zhí)行的線程暫停拘荡,但它不會阻塞該線程臼节。yield只是讓當(dāng)前線程暫停一下,讓系統(tǒng)的線程調(diào)度器重新調(diào)度一次珊皿,完全可能的情況是:當(dāng)某個線程調(diào)用了yield方法暫停之后网缝,線程調(diào)度器又將其調(diào)度出來重新執(zhí)行。
實際上蟋定,當(dāng)某個線程調(diào)用了yield方法暫停之后途凫,只有優(yōu)先級與當(dāng)前線程相同,或者優(yōu)先級比當(dāng)前線程更高的溢吻、就緒狀態(tài)的線程才會獲得執(zhí)行的機會维费。

sleep方法和yield方法的區(qū)別

  • sleep方法暫停當(dāng)前線程后,會給其他線程執(zhí)行機會促王,不會理會其他線程的優(yōu)先級犀盟。但yield方法只會給優(yōu)先級相同,或優(yōu)先級更高的線程執(zhí)行機會蝇狼。
  • sleep方法會將線程轉(zhuǎn)入阻塞狀態(tài)阅畴,直到經(jīng)過阻塞時間才會轉(zhuǎn)入就緒狀態(tài)。而yield不會將線程轉(zhuǎn)入阻塞狀態(tài)迅耘,它只是強制當(dāng)前線程進入就緒狀態(tài)贱枣。因此完全有可能某個線程調(diào)用yield方法暫停之后,立即再次獲得處理器資源被執(zhí)行颤专。
  • sleep方法聲明拋出了InterruptedException異常纽哥,所以調(diào)用sleep方法時要么捕捉該異常,要么顯式聲明拋出該異常栖秕。而yield方法則沒有聲明拋出任何異常春塌。
  • sleep方法比yield方法有更好的可移植性,通常不要依靠yield來控制并發(fā)線程的執(zhí)行簇捍。

線程優(yōu)先級
每個線程執(zhí)行時都有具有一定的優(yōu)先級只壳,優(yōu)先級高的線程獲得較多的執(zhí)行機會,而優(yōu)先級低的線程則獲得較少的執(zhí)行機會暑塑。
每個線程默認(rèn)的優(yōu)先級都與創(chuàng)建它的父線程具有相同的優(yōu)先級吼句,默認(rèn)情況下,main線程的具有普通優(yōu)先級事格,由main線程創(chuàng)建的子線程也有普通優(yōu)先級惕艳。
Thread提供了setPriority(int newPriority)和getPriority()方法來設(shè)置和返回指定線程的優(yōu)先級况毅,其中setPriority方法的參數(shù)可以是一個整數(shù),范圍是1~10之間尔艇,也可以使Thread類的三個靜態(tài)常量:
MAX_PRIORITY:其值是10尔许。
MIN_PRIORITY:其值是1。
NORM_PRIORITY:其值是5终娃。

同步代碼塊
Java的多線程支持引入了同步監(jiān)視器來解決這個問題味廊,使用同步監(jiān)視器的通用方法就是同步代碼塊。
synchronized后括號里的obj就是同步監(jiān)視器棠耕,上面代碼的含義是:線程開始執(zhí)行同步代碼塊之前余佛,必須先獲得對同步監(jiān)視器的鎖定。
選擇監(jiān)視器的目的:阻止兩條線程對同一個共享資源進行并發(fā)訪問窍荧。因此通常推薦使用可能被并發(fā)訪問的共享資源充當(dāng)同步監(jiān)視器辉巡。對于上面的取錢模擬程序,我們應(yīng)該考慮使用賬戶(account)作為同步監(jiān)視器蕊退。

同步方法
Java的多線程安全支持還提供了同步方法郊楣,同步方法就是使用synchronized關(guān)鍵字來修飾某個方法,則該方法成為同步方法瓤荔。對于同步方法而言净蚤,無需顯式指定同步監(jiān)視器,同步方法的同步監(jiān)視器是this输硝,也就是該對象本身今瀑。

線程安全的類

  • 不要對線程安全類的所有方法都進行同步,只對那些會改變競爭資源(競爭資源也就是共享資源)的方法進行同步点把。例如上面的Account類中accountNo屬性就無需同步橘荠,所以程序只對draw方法進行同步控制。
  • 如果可變類有兩種運行環(huán)境:單線程環(huán)境和多線程環(huán)境郎逃,則應(yīng)該為該可變類提供兩種版本:線程不安全版本和線程安全版本哥童。在單線程環(huán)境中使用線程不安全版本以保證性能,在多線程環(huán)境中使用線程安全版本衣厘。

釋放同步監(jiān)視器
線程會在如下幾種情況下釋放對同步監(jiān)視器的鎖定:

  • 當(dāng)前線程的同步方法如蚜、同步代碼塊執(zhí)行結(jié)束压恒,當(dāng)前線程即釋放同步監(jiān)視器影暴。
  • 當(dāng)線程在同步代碼塊、同步方法中遇到break探赫、return終止了該代碼塊型宙、該方法的繼續(xù)執(zhí)行,當(dāng)前線程將會釋放同步監(jiān)視器伦吠。
  • 當(dāng)線程在同步代碼塊妆兑、同步方法中出現(xiàn)了未處理的Error或Exception魂拦,導(dǎo)致了該代碼塊、該方法異常結(jié)束時將會釋放同步監(jiān)視器搁嗓。
  • 當(dāng)線程執(zhí)行同步代碼塊或同步方法時芯勘,程序執(zhí)行了同步監(jiān)視器對象的wait()方法,則當(dāng)前線程暫停腺逛,并釋放同步監(jiān)視器荷愕。

同步鎖(Lock)
Lock是控制多個線程對共享資源進行訪問的工具。通常棍矛,鎖提供了對共享資源的獨占訪問安疗,每次只能有一個線程對Lock對象加鎖,線程開始訪問共享資源之前應(yīng)先獲得Lock對象够委。不過荐类,某些鎖可能允許對共享資源并發(fā)訪問,如 ReadWriteLock(讀寫鎖)茁帽。當(dāng)然玉罐,在實現(xiàn)線程安全的控制中,通常喜歡使用ReentrantLock(可重入鎖)潘拨。使用該Lock對象可以顯式地加鎖厌小、釋放鎖。
ReentrantLock鎖具有可重入性战秋,也就是說線程可以對它已經(jīng)加鎖的ReentrantLock鎖再次加鎖璧亚,ReentrantLock對象會維持一個計數(shù)器來追蹤lock方法的嵌套調(diào)用,線程在每次調(diào)用lock()方法加鎖后脂信,必須顯式調(diào)用unlock()方法來釋放鎖癣蟋,所以一段被鎖保護的代碼可以調(diào)用另一個被相同鎖保護的方法

死鎖
當(dāng)兩個線程相互等待對方釋放同步監(jiān)視器時就會發(fā)生死鎖,Java虛擬機沒有監(jiān)測狰闪、也沒有采用措施來處理死鎖情況疯搅,所以多線程編程時應(yīng)該采取措施避免死鎖的出現(xiàn)。一旦出現(xiàn)死鎖埋泵,整個程序既不會發(fā)生任何異常幔欧,也不會給出任何提示,只是所有線程處于阻塞狀態(tài)丽声,無法繼續(xù)礁蔗。

線程的協(xié)調(diào)運行
以借助于Object類提供的wait()、notify()和notifyAll()三個方法雁社,這三個方法并不屬于Thread類浴井,而是屬于Object類。但這三個方法必須同步監(jiān)視器對象調(diào)用霉撵。
關(guān)于這三個方法的解釋如下:

  • wait():導(dǎo)致當(dāng)前線程等待磺浙,直到其他線程調(diào)用該同步監(jiān)視器的notify()方法或notifyAll()方法來喚醒該線程洪囤。該wait()方法有三種形式:無時間參數(shù)的wait(一直等待,直到其他線程通知)撕氧,帶毫秒?yún)?shù)的wait和帶毫秒瘤缩、微秒?yún)?shù)的wait(這兩種方法都是等待指定時間后自動蘇醒)。調(diào)用wait()方法的當(dāng)前線程會釋放對該同步監(jiān)視器的鎖定伦泥。
  • notify():喚醒在此同步監(jiān)視器上等待的單個線程款咖。如果所有線程都在此同步監(jiān)視器上等待,則會選擇喚醒其中一個線程奄喂。選擇是任意性的铐殃。只有當(dāng)前線程放棄對該同步監(jiān)視器的鎖定后(使用wait()方法),才可以執(zhí)行被喚醒的線程跨新。
  • notifyAll():喚醒在此同步監(jiān)視器上等待的所有線程富腊。只有當(dāng)前線程放棄對該同步監(jiān)視器的鎖定后,才可以執(zhí)行被喚醒的線程域帐。

使用條件變量控制協(xié)調(diào)

當(dāng)使用Lock對象來保證同步時赘被,Java提供了一個Condition類來保持協(xié)調(diào),使用Condition可以讓那些已經(jīng)得到Lock對象肖揣、卻無法繼續(xù)執(zhí)行的線程釋放Lock對象民假,Condtion對象也可以喚醒其他處于等待的線程。
Condition 將同步監(jiān)視鎖方法(wait龙优、notify 和 notifyAll)分解成截然不同的對象羊异,以便通過將這些對象與Lock對象組合使用,為每個對象提供多個等待集(wait-set)彤断。在這種情況下野舶,Lock 替代了同步方法或同步代碼塊,Condition替代了同步監(jiān)視鎖的功能宰衙。
Condition實例實質(zhì)上被綁定在一個Lock對象上平道。要獲得特定Lock實例的Condition實例,調(diào)用Lock對象newCondition()方法即可供炼。Condtion類提供了如下三個方法:

  • await():類似于隱式同步監(jiān)視器上的wait()方法一屋,導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用該Condtion的signal ()方法或signalAll ()方法來喚醒該線程袋哼。該await方法有更多變體:long awaitNanos(long nanosTimeout)冀墨、void awaitUninterruptibly()、awaitUntil(Date deadline)等先嬉,可以完成更豐富的等待操作轧苫。
  • signal ():喚醒在此Lock對象上等待的單個線程。如果所有線程都在該Lock對象上等待疫蔓,則會選擇喚醒其中一個線程含懊。選擇是任意性的。只有當(dāng)前線程放棄對該Lock對象的鎖定后(使用await()方法)衅胀,才可以執(zhí)行被喚醒的線程岔乔。
  • signalAll():喚醒在此Lock對象上等待的所有線程。只有當(dāng)前線程放棄對該該Lock對象的鎖定后滚躯,才可以執(zhí)行被喚醒的線程雏门。

線程池
系統(tǒng)啟動一個新線程的成本是比較高的,因為它涉及到與操作系統(tǒng)交互掸掏。在這種情形下茁影,使用線程池可以很好地提高性能,尤其是當(dāng)程序中需要創(chuàng)建大量生存期很短暫的線程時丧凤,更應(yīng)該考慮使用線程池募闲。
與數(shù)據(jù)庫連接池類似的是,線程池在系統(tǒng)啟動時即創(chuàng)建大量空閑的線程愿待,程序?qū)⒁粋€Runnable對象傳給線程池浩螺,線程池就會啟動一條線程來執(zhí)行該對象的run方法,當(dāng)run方法執(zhí)行結(jié)束后仍侥,該線程并不會死亡要出,而是再次返回線程池中成為空閑狀態(tài),等待執(zhí)行下一個Runnable對象的run方法农渊。

使用線程池的步驟

  1. 調(diào)用Executors類的靜態(tài)工廠方法創(chuàng)建一個ExecutorService對象或ScheduledExecutorService對象患蹂,其中前者代表簡單的線程池,后者代表能以任務(wù)調(diào)度方式執(zhí)行線程的線程池砸紊。
  2. 創(chuàng)建Runnable實現(xiàn)類或Callable實現(xiàn)類的實例况脆,作為線程執(zhí)行任務(wù)。
  3. 調(diào)用ExecutorService對象的submit方法來提交Runnable實例或Callable實例批糟;或調(diào)用ScheduledExecutorService的schedule來執(zhí)行線程格了。
  4. 當(dāng)不想提交任何任務(wù)時調(diào)用ExecutorService對象的shutdown方法來關(guān)閉線程池。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末徽鼎,一起剝皮案震驚了整個濱河市盛末,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌否淤,老刑警劉巖悄但,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異石抡,居然都是意外死亡檐嚣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門啰扛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嚎京,“玉大人嗡贺,你說我怎么就攤上這事“暗郏” “怎么了诫睬?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帕涌。 經(jīng)常有香客問我摄凡,道長,這世上最難降的妖魔是什么蚓曼? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任亲澡,我火速辦了婚禮,結(jié)果婚禮上纫版,老公的妹妹穿的比我還像新娘床绪。我一直安慰自己,他們只是感情好捎琐,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布会涎。 她就那樣靜靜地躺著,像睡著了一般瑞凑。 火紅的嫁衣襯著肌膚如雪末秃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天籽御,我揣著相機與錄音练慕,去河邊找鬼。 笑死技掏,一個胖子當(dāng)著我的面吹牛铃将,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哑梳,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼劲阎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸠真?” 一聲冷哼從身側(cè)響起悯仙,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吠卷,沒想到半個月后锡垄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡祭隔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年货岭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡千贯,死狀恐怖屯仗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丈牢,我是刑警寧澤祭钉,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布瞄沙,位于F島的核電站己沛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏距境。R本人自食惡果不足惜申尼,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垫桂。 院中可真熱鬧师幕,春花似錦、人聲如沸诬滩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疼鸟。三九已至后控,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間空镜,已是汗流浹背浩淘。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吴攒,地道東北人张抄。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像洼怔,于是被迫代替她去往敵國和親署惯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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