多線程并發(fā)總結(jié)錄(一) --線程進程基礎(chǔ)

線程基礎(chǔ)捞稿,線程之間共享與協(xié)作

1.基礎(chǔ)概念

進程概念:進程是程序運行資源分配的最小單位

進程是操作系統(tǒng)進行資源分配的最小單位,其中資源包括:CPU晴竞、內(nèi)存空間寒矿、磁盤IO 等,同一進程中的多條線程共享該進程中的全部系統(tǒng)資源,而進程和進程之間是相互獨立的衅澈。進程是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位嗅榕。進程是程序在計算機上的一次執(zhí)行活動室奏。當(dāng)你運行一個程序,你就啟動了一個進程火焰。顯然,程序是死的、靜態(tài)的,進程是活的胧沫、動態(tài)的昌简。進程可以分為系統(tǒng)進程和用戶進程占业。凡是用于完成操作系統(tǒng)的各種功能的進程就是系統(tǒng)進程,它們就是處于運行狀態(tài)下的操作系統(tǒng)本身,用戶進程就是所有由你啟動的進程。

線程概念:線程是CPU 調(diào)度的最小單位,必須依賴于進程而存在

線程是進程的一個實體,是CPU 調(diào)度和分派的基本單位,它是比進程更小的江场、能獨立運行的基本單位纺酸。線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。

2. CPU 核心數(shù)和線程數(shù)的關(guān)系

多核心:也指單芯片多處理器( Chip Multiprocessors,簡稱CMP),CMP 是由美國斯坦福大學(xué)提出的,其思想是將大規(guī)模并行處理器中的SMP(對稱多處理器)集成到同一芯片內(nèi),各個處理器并行執(zhí)行不同的進程址否。這種依靠多個CPU 同時并行地運行程序是實現(xiàn)超高速計算的一個重要方向,稱為并行處理
多線程: Simultaneous Multithreading.簡稱SMT.讓同一個處理器上的多個線程同步執(zhí)行并共享處理器的執(zhí)行資源餐蔬。
核心數(shù)、線程數(shù):目前主流CPU 都是多核的佑附。增加核心數(shù)目就是為了增加線程數(shù),因為操作系統(tǒng)是通過線程來執(zhí)行任務(wù)的,一般情況下它們是1:1 對應(yīng)關(guān)系,也就是說四核CPU 一般擁有四個線程樊诺。但Intel 引入超線程技術(shù)后,使核心數(shù)與線程數(shù)形成1:2 的關(guān)系

3. CPU時間片輪轉(zhuǎn)機制(RR 調(diào)度)

? 時間片輪轉(zhuǎn)法(Round-Robin,RR)主要用于分時系統(tǒng)中的進程調(diào)度音同。為了實現(xiàn)輪轉(zhuǎn)調(diào)度词爬,系統(tǒng)把所有就緒進程按先入先出的原則排成一個隊列。新來的進程加到就緒隊列末尾权均。每當(dāng)執(zhí)行進程調(diào)度時顿膨,進程調(diào)度程序總是選出就緒隊列的隊首進程,讓它在 CPU 上運行一個時間片的時間叽赊。時間片是一個小的時間單位恋沃,通常為 10~100ms 數(shù)量級。當(dāng)進程用完分給它的時間片后必指,系統(tǒng)的計時器發(fā)出時鐘中斷囊咏,調(diào)度程序便停止該進程的運行,把它放入就緒隊列的末尾塔橡;然后梅割,把 CPU 分給就緒隊列的隊首進程,同樣也讓它運行一個時間片葛家,如此往復(fù)户辞。
?

3.1 進程調(diào)度

? 采用此算法的系統(tǒng),其程序就緒隊列往往按進程到達的時間來排序癞谒。進程調(diào)度程序總是選擇就緒隊列中的第一個進程咆课,也就是說按照先來先服務(wù)原則調(diào)度,但一旦進程占用處理機則僅使用一個時間片扯俱。在使用先一個時間片后,進程還沒有完成其運行喇澡,它必須釋放出處理機給下一個就緒的進程迅栅,而被搶占的進程返回到就緒隊列的末尾重新排隊等待再次運行。

處理器同一個時間只能處理一個任務(wù)晴玖。處理器在處理多任務(wù)的時候读存,就要看請求的時間順序为流,如果時間一致,就要進行預(yù)測让簿。挑到一個任務(wù)后敬察,需要若干步驟才能做完,這些步驟中有些需要處理器參與尔当,有些不需要(如磁盤控制器的存儲過程)莲祸。不需要處理器處理的時候,這部分時間就要分配給其他的進程椭迎。原來的進程就要處于等待的時間段上锐帜。經(jīng)過周密分配時間,宏觀上就象是多個任務(wù)一起運行一樣畜号,但微觀上是有先后的缴阎,就是時間片輪換。

3.2 實現(xiàn)思想

? 時間片輪轉(zhuǎn)算法的基本思想是简软,系統(tǒng)將所有的就緒進程按先來先服務(wù)算法的原則蛮拔,排成一個隊列,每次調(diào)度時痹升,系統(tǒng)把處理機分配給隊列首進程建炫,并讓其執(zhí)行一個時間片。當(dāng)執(zhí)行的時間片用完時视卢,由一個計時器發(fā)出時鐘中斷請求踱卵,調(diào)度程序根據(jù)這個請求停止該進程的運行,將它送到就緒隊列的末尾据过,再把處理機分給就緒隊列中新的隊列首進程惋砂,同時讓它也執(zhí)行一個時間片。

3.3 時間片設(shè)置多少合適

? 從一個進程切換到另一個進程是需要定時間的,包括保存和裝入寄存器值及內(nèi)存映像,更新各種表格和隊列等绳锅。假如進程切( processwitch),有時稱為上下文切換( context switch),需要5ms,再假設(shè)時間片設(shè)為20ms,則在做完20ms 有用的工作之后,CPU 將花費5ms 來進行進程切換西饵。CPU 時間的20%被浪費在了管理開銷上了。
? 為了提高CPU 效率,我們可以將時間片設(shè)為500ms鳞芙。這時浪費的時間只有0.1%眷柔。但考慮到在一個分時系統(tǒng)中,如果有10 個交互用戶幾乎同時按下回車鍵,將發(fā)生什么情況?假設(shè)所有其他進程都用足它們的時間片的話,最后一個不幸的進程不得不等待5s 才獲得運行機會。多數(shù)用戶無法忍受一條簡短命令要5 才能做出響應(yīng)原朝。
? 結(jié)論總結(jié)如下: 時間片設(shè)得太短會導(dǎo)致過多的進程切換,降低了CPU 效率:而設(shè)得太長又可能引起對短的交互請求的響應(yīng)變差驯嘱。將時間片設(shè)為100ms 通常是一個比較合理的折衷。

4. 并發(fā)和并行的區(qū)別

? **并發(fā): **指應(yīng)用能夠交替執(zhí)行不同的任務(wù),比如單CPU 核心下執(zhí)行多線程并非是同時執(zhí)行多個任務(wù),如果你開兩個線程執(zhí)行,就是在你幾乎不可能察覺到的速度不斷去切換這兩個任務(wù),已達到"同時執(zhí)行效果",其實并不是的,只是計算機的速度太快,我們無法察覺到而已.
? 并行: 指應(yīng)用能夠同時執(zhí)行不同的任務(wù),例:吃飯的時候可以邊吃飯邊打電話,這兩件事情可以同時執(zhí)行
? **兩者區(qū)別: **一個是交替執(zhí)行,一個是同時執(zhí)行.

5. 多線程程序需要注意事項

5.1 線程之間的安全性

? 在同一個進程里面的多線程是資源共享的,也就是都可以訪問同一個內(nèi)存地址當(dāng)中的一個變量喳坠。例如:若每個線程中對全局變量鞠评、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的:若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。

5.2 線程之間的死鎖

? 為了解決線程之間的安全性引入了Java 的鎖機制,而一不小心就會產(chǎn)生Java線程死鎖的多線程問題,因為不同的線程都在等待那些根本不可能被釋放的鎖,從而導(dǎo)致所有的工作都無法完成壕鹉。

5.3 線程太多了會將服務(wù)器資源耗盡形成死機當(dāng)機

? 線程數(shù)太多有可能造成系統(tǒng)創(chuàng)建大量線程而導(dǎo)致消耗完系統(tǒng)內(nèi)存以及CPU的“過渡切換”,造成系統(tǒng)的死機剃幌。

? 針對多線程程序可能耗盡資源的問題聋涨,在我們的程序中應(yīng)該使用線程池來管理線程、使用數(shù)據(jù)庫連接池來管理數(shù)據(jù)庫連接负乡,用對象池來管理對象牍白,防止對象經(jīng)常創(chuàng)建和回收導(dǎo)致內(nèi)存抖動。

6. Java程序與生俱來就是多線程程序

? 寫一個最簡單的demo抖棘,看看java虛擬機會為這個demo開辟多少個線程

public static void main(String[] args) {
        // Java虛擬機線程管理接口
        ThreadMXBean tBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfos = tBean.dumpAllThreads(false, false);
        for (ThreadInfo info:threadInfos) {
            System.out.println("thread id:[" + info.getThreadId() 
            + "]; thread name:[" + info.getThreadName() + "]");
        }
}
運行結(jié)果:
thread id:[5]; thread name:[Attach Listener]  //內(nèi)存dump茂腥,線程dump,類信息統(tǒng)計钉答,獲取系統(tǒng)屬性等
thread id:[4]; thread name:[Signal Dispatcher]//分發(fā)處理發(fā)送給JVM 信號的線程
thread id:[3]; thread name:[Finalizer]         //調(diào)用對象finalize 方法的線程
thread id:[2]; thread name:[Reference Handler] //清除Reference 的線程
thread id:[1]; thread name:[main]              //主程序础芍,用戶程序入口

7. 線程的啟動和中止

7.1線程啟動

? 線程的啟動方式有兩種:1.繼承Thread類并且實現(xiàn)run方法的方式;2.實現(xiàn)Runnable接口的方式

7.1.1 繼承Thread類并且實現(xiàn)run方法的方式
//摘自java.lang.Thread
    There are two ways to create a new thread of execution. One is to declare a class to be a subclass of <code>Thread</code>. This subclass should override the <code>run</code> method of class <code>Thread</code>. An instance of the subclass can then be allocated and started. 
        
 class PrimeThread extends Thread {
    long minPrime;

    PrimeThread(long minPrime) {
        this.minPrime = minPrime;
    }

    @Override
    public void run() {
        // compute primes larger than minPrime
    }
}

PrimeThread p = new PrimeThread(143);
p.start();
7.1.2 實現(xiàn)Runnable的方法
    The other way to create a thread is to declare a class implements the <code>run</code> method. An instance of the class can then be allocated, passed as an argument when creating <code>Thread</code>, and started.

class PrimeRun implements Runnable {
   long minPrime;        
   PrimeRun(long minPrime) {
      this.minPrime = minPrime;
   }

   public void run() {
      // compute primes larger than minPrime
   }
}

PrimeRun p = new PrimeRun(143);
new Thread(p).start();
7.1.3 Thread 和Runnable 的區(qū)別

? Thread 是Java 里對線程的唯一抽象数尿,Runnable 只是對任務(wù)(業(yè)務(wù)邏輯)的抽象仑性。Thread 可以接受任意一個Runnable 的實例并執(zhí)行。

7.2 線程的中止

? 線程的中止有兩種可能右蹦,要么是run()方法執(zhí)行完畢诊杆,要么是程序人為中止執(zhí)行。

? 這里重點討論程序人為中止的手段何陆。從Thread類中晨汹,可以發(fā)現(xiàn)中止線程執(zhí)行的方法有 suspend()、resume()和stop()贷盲,但是這些方法都是過時的淘这,也是官方不建議使用的,因為使用以上三種方法來暴力停止線程執(zhí)行巩剖,可能會造成死鎖的問題铝穷。以suspend()為例,當(dāng)調(diào)用了suspend()之后佳魔,線程不會釋放已經(jīng)占有的資源(比如鎖)曙聂,而是占有著資源進入睡眠狀態(tài),這樣容易引發(fā)死鎖問題鞠鲜。stop()和resume()原理是一樣的宁脊,可能會導(dǎo)致程序死鎖,嚴(yán)重會導(dǎo)致自己或者其他程序ANR贤姆。所以考慮到這種嚴(yán)重的副作用榆苞,官方不建議使用以上三種方法停止線程執(zhí)行任務(wù)。

? 那么如何比較優(yōu)雅地中止線程呢霞捡?

? 因為JDK中的線程是協(xié)作式的坐漏,而不是搶占式的,否則線程發(fā)起了中斷,線程可以不理會此中斷仙畦。所以配合使用interrupt()和isInterrupted()來中斷線程執(zhí)行。interrupt()方法只是將中斷標(biāo)志位置位了音婶,而不是強行中止線程慨畸,源碼如下:

public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

? 那么正確的使用方法如下

public static class UserThread extends Thread{
    @Override
    public void run() {
        super.run();
        String name = Thread.currentThread().getName();
        //和諧停止標(biāo)志位
        while (!isInterrupted()){
            System.out.println(name + "running ! isInterrupt stats = " + isInterrupted());
        }
        System.out.println(name + " exit ! isInterrupt stats = " + isInterrupted());
    }
}

public static void main(String[] args) throws InterruptedException {
    UserThread userThread = new UserThread();
    userThread.start();

    Thread.sleep(3);
    //通知要中斷,但是如果還有在執(zhí)行的任務(wù)衣式,不會真正結(jié)束
    userThread.interrupt();

}
7.3 我們可以自定義標(biāo)志位來管理線程么寸士?

? 不建議自定義一個取消標(biāo)志位來中止線程的運行。因為run 方法里有阻塞調(diào)用時會無法很快檢測到取消標(biāo)志碴卧,線程必須從阻塞調(diào)用返回后弱卡,才會檢查這個取消標(biāo)志。這種情況下住册,使用中斷會更好婶博,因為,

  • 一荧飞、一般的阻塞方法凡人,如sleep 等本身就支持中斷的檢查,

  • 二叹阔、檢查中斷位的狀態(tài)和檢查取消標(biāo)志位沒什么區(qū)別挠轴,用中斷位的狀態(tài)還可以避免聲明取消標(biāo)志位,減少資源的消耗耳幢。

    如果一個線程處于了阻塞狀態(tài)(如線程調(diào)用了thread.sleep岸晦、thread.join、thread.wait 等)睛藻,則線程在檢查中斷標(biāo)示時如果發(fā)現(xiàn)中斷標(biāo)示為true启上,則會在這些阻塞方法調(diào)用處拋InterruptedException 異常,并且在拋出異常后會立即將線程的中斷標(biāo)示位清除修档,即重新設(shè)置為false碧绞。

8. 關(guān)于線程的其他點點滴滴

8.1 run()和start()關(guān)系

? Thread 類是Java 里對線程概念的抽象,可以這樣理解:我們通過new Thread()其實只是new 出一個Thread 的實例吱窝,還沒有操作系統(tǒng)中真正的線程掛起鉤來讥邻。只有執(zhí)行了start()方法后,才實現(xiàn)了真正意義上的啟動線程院峡。start()方法讓一個線程進入就緒隊列等待分配cpu兴使,分到cpu 后才調(diào)用實現(xiàn)的run()方法,start()方法不能重復(fù)調(diào)用照激,如果重復(fù)調(diào)用會拋出異常发魄。而run 方法是業(yè)務(wù)邏輯實現(xiàn)的地方,本質(zhì)上和任意一個類的任意一個成員方法并沒有任何區(qū)別,可以重復(fù)執(zhí)行励幼,也可以被單獨調(diào)用汰寓。

8.2 Thread中的其他方法
8.2.1 yield()方法

? 使當(dāng)前線程讓出CPU 占有權(quán),但讓出的時間是不可設(shè)定的苹粟。也不會釋放鎖資源有滑。注意:并不是每個線程都需要這個鎖的,而且執(zhí)行yield( )的線程不一定就會持有鎖嵌削,我們完全可以在釋放鎖后再調(diào)用yield 方法毛好。所有執(zhí)行yield()的線程有可能在進入到就緒狀態(tài)后會被操作系統(tǒng)再次選中馬上又被執(zhí)行。

8.2.2 join()方法

? (1) 把指定的線程加入到當(dāng)前線程苛秕,可以將兩個交替執(zhí)行的線程合并為順序執(zhí)行肌访。比如在線程B 中調(diào)用了線程A 的Join()方法,直到線程A 執(zhí)行完畢后艇劫,才會繼續(xù)執(zhí)行線程B吼驶。

public static void main(String[] args) throws InterruptedException {
        ThreadJoinTest threadJoinTest1 = new ThreadJoinTest("A");
        ThreadJoinTest threadJoinTest2 = new ThreadJoinTest("B");
        ThreadJoinTest threadJoinTest3 = new ThreadJoinTest("C");
        threadJoinTest1.start();
        threadJoinTest1.join();
        threadJoinTest2.start();
        threadJoinTest2.join();
        threadJoinTest3.start();
        threadJoinTest3.join();
    }

以上事例把異步執(zhí)行的事情,變成都在主線程執(zhí)行的同步事件了港准,雖然這樣做就相當(dāng)于不開線程旨剥,主要是為了演示合并成串行執(zhí)行。

? (2) 從另一個角度上講浅缸,join()可以讓某個子線程執(zhí)行完畢之后在執(zhí)行主線程的代碼轨帜。相當(dāng)于“阻塞”主線程,等子線程執(zhí)行完成之后衩椒,在執(zhí)行主線程代碼蚌父。

public static void main(String[] args) throws InterruptedException {
    ThreadJoinTest threadJoinTest1 = new ThreadJoinTest("A");
    threadJoinTest1.start();
    threadJoinTest1.join(); // 當(dāng)join()執(zhí)行完之后才能執(zhí)行以下代碼。
    System.out.println("我只能在join()執(zhí)行完成之后才能執(zhí)行毛萌!");
}
8.2.3 線程的生命周期以及基本狀態(tài)
線程生命周期

? 關(guān)于Java中線程的生命周期苟弛,首先看一下下面這張較為經(jīng)典的圖:

線程狀態(tài)切換.png
線程的基本狀態(tài)

新建狀態(tài)(New):當(dāng)線程對象對創(chuàng)建后,即進入了新建狀態(tài)阁将,如:Thread t = new MyThread();

就緒狀態(tài)(Runnable):當(dāng)調(diào)用線程對象的start()方法(t.start();)膏秫,線程即進入就緒狀態(tài)。處于就緒狀態(tài)的線程做盅,只是說明此線程已經(jīng)做好了準(zhǔn)備缤削,隨時等待CPU調(diào)度執(zhí)行,并不是說執(zhí)行了t.start()此線程立即就會執(zhí)行吹榴;

運行狀態(tài)(Running):當(dāng)CPU開始調(diào)度處于就緒狀態(tài)的線程時亭敢,此時線程才得以真正執(zhí)行,即進入到運行狀態(tài)图筹。注:就 緒狀態(tài)是進入到運行狀態(tài)的唯一入口帅刀,也就是說让腹,線程要想進入運行狀態(tài)執(zhí)行,首先必須處于就緒狀態(tài)中扣溺;

阻塞狀態(tài)(Blocked):處于運行狀態(tài)中的線程由于某種原因骇窍,暫時放棄對CPU的使用權(quán),停止執(zhí)行锥余,此時進入阻塞狀態(tài)像鸡,直到其進入到就緒狀態(tài),才 有機會再次被CPU調(diào)用以進入到運行狀態(tài)哈恰。根據(jù)阻塞產(chǎn)生的原因不同,阻塞狀態(tài)又可以分為三種:

1.等待阻塞:運行狀態(tài)中的線程執(zhí)行wait()方法志群,使本線程進入到等待阻塞狀態(tài)着绷;

2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態(tài)锌云;

3.其他阻塞 -- 通過調(diào)用線程的sleep()或join()或發(fā)出了I/O請求時荠医,線程會進入到阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時桑涎、join()等待線程終止或者超時彬向、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)攻冷。

死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法娃胆,該線程結(jié)束生命周期。

8.2.4 線程優(yōu)先級

? 在Java 線程中等曼,通過一個整型成員變量priority 來控制優(yōu)先級里烦,優(yōu)先級的范圍從1~10,在線程構(gòu)建的時候可以通過setPriority(int)方法來修改優(yōu)先級禁谦,默認(rèn)優(yōu)先級是5胁黑,優(yōu)先級高的線程分配時間片的數(shù)量要多于優(yōu)先級低的線程。

? 設(shè)置線程優(yōu)先級時州泊,針對頻繁阻塞(休眠或者I/O 操作)的線程需要設(shè)置較高優(yōu)先級丧蘸,而偏重計算(需要較多CPU 時間或者偏運算)的線程則設(shè)置較低的優(yōu)先級,確保處理器不會被獨占遥皂。

8.2.5 守護線程

? Daemon(守護)線程是一種支持型線程力喷,因為它主要被用作程序中后臺調(diào)度以及支持性工作。這意味著渴肉,當(dāng)一個Java 虛擬機中存在Daemon 線程的時候冗懦,當(dāng)主線程退出之后,守護線程也會跟著退出仇祭。比如垃圾回收線程就是Daemon 線程披蕉,但是在Java 虛擬機退出時Daemon 線程中的finally 塊并不一定會執(zhí)行。在構(gòu)建Daemon 線程時,不能依靠finally 塊中的內(nèi)容來確保執(zhí)行關(guān)閉或清理資源的邏輯没讲。

public static void main(String[] args) throws InterruptedException {
    final Thread thread = new Thread(){
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 5; i++) {
                System.out.println(getName() + "----->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };
    //如果該線程是守護線程眯娱,那么main線程執(zhí)行完畢之后,守護線程一起結(jié)束
    //如果不是守護線程爬凑,Main線程會等待該線程執(zhí)行結(jié)束后結(jié)束徙缴。
    //如果是守護線程,子線程大于main的時間嘁信,main執(zhí)行完了就結(jié)束于样,不管子線程。
    thread.setDaemon(true);
    thread.start();

    // 設(shè)置3000 6000觀察守護線程內(nèi)打印情況可以看出守護線程的生命周期
    Thread.sleep(3000);
}

測試用例代碼見: git@github.com:oujie123/UnderstandingOfThread.git

參考資料:

CPU時間片輪轉(zhuǎn)機制

線程基礎(chǔ)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潘靖,一起剝皮案震驚了整個濱河市穿剖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卦溢,老刑警劉巖糊余,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異单寂,居然都是意外死亡贬芥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門宣决,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蘸劈,“玉大人,你說我怎么就攤上這事尊沸£鞘保” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵椒丧,是天一觀的道長壹甥。 經(jīng)常有香客問我,道長壶熏,這世上最難降的妖魔是什么句柠? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮棒假,結(jié)果婚禮上溯职,老公的妹妹穿的比我還像新娘。我一直安慰自己帽哑,他們只是感情好谜酒,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妻枕,像睡著了一般僻族。 火紅的嫁衣襯著肌膚如雪粘驰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天述么,我揣著相機與錄音蝌数,去河邊找鬼。 笑死度秘,一個胖子當(dāng)著我的面吹牛顶伞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剑梳,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼唆貌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了垢乙?” 一聲冷哼從身側(cè)響起挠锥,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侨赡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粱侣,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡羊壹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了齐婴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片油猫。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柠偶,靈堂內(nèi)的尸體忽然破棺而出情妖,到底是詐尸還是另有隱情,我是刑警寧澤诱担,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布毡证,位于F島的核電站,受9級特大地震影響蔫仙,放射性物質(zhì)發(fā)生泄漏料睛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一摇邦、第九天 我趴在偏房一處隱蔽的房頂上張望恤煞。 院中可真熱鬧,春花似錦施籍、人聲如沸居扒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喜喂。三九已至瓤摧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間夜惭,已是汗流浹背姻灶。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诈茧,地道東北人产喉。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像敢会,于是被迫代替她去往敵國和親曾沈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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