關(guān)于synchronized和ReentrantLock之多線程同步詳解

本篇文章總結(jié)關(guān)于多線程編程的一些知識(shí)點(diǎn)骗污,這其中兩個(gè)重要的部分就是對于synchronized和ReentrantLock的使用和介紹滤灯。

一、線程同步問題的產(chǎn)生及解決方案

問題的產(chǎn)生:
Java允許多線程并發(fā)控制,當(dāng)多個(gè)線程同時(shí)操作一個(gè)可共享的資源變量時(shí)(如數(shù)據(jù)的增刪改查)焦履,將會(huì)導(dǎo)致數(shù)據(jù)不準(zhǔn)確贸典,相互之間產(chǎn)生沖突视卢。
如下例:假設(shè)有一個(gè)賣票系統(tǒng),一共有100張票廊驼,有4個(gè)窗口同時(shí)賣据过。

public class Ticket implements Runnable {
    // 當(dāng)前擁有的票數(shù)
    private int num = 100;

    public void run() {
        while (true) {
            if (num > 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
                // 輸出賣票信息
                System.out.println(Thread.currentThread().getName() + ".....sale...." + num--);
            }
        }
    }
}

public static void main(String[] args) {
    Ticket t = new Ticket();//創(chuàng)建一個(gè)線程任務(wù)對象。   
        //創(chuàng)建4個(gè)線程同時(shí)賣票  
        Thread t1 = new Thread(t);  
        Thread t2 = new Thread(t);  
        Thread t3 = new Thread(t);  
        Thread t4 = new Thread(t);  
        //啟動(dòng)線程  
        t1.start();  
        t2.start();  
        t3.start();  
        t4.start();  
    }

輸出部分結(jié)果:

Thread-1.....sale....2
Thread-0.....sale....3
Thread-2.....sale....1
Thread-0.....sale....0
Thread-1.....sale....0
Thread-3.....sale....1

顯然上述結(jié)果是不合理的妒挎,對于同一張票進(jìn)行了多次售出绳锅。這就是多線程情況下,出現(xiàn)了數(shù)據(jù)“臟讀”情況酝掩。即多個(gè)線程訪問余票num時(shí)鳞芙,當(dāng)一個(gè)線程獲得余票的數(shù)量,要在此基礎(chǔ)上進(jìn)行-1的操作之前期虾,其他線程可能已經(jīng)賣出多張票原朝,導(dǎo)致獲得的num不是最新的,然后-1后更新的數(shù)據(jù)就會(huì)有誤镶苞。這就需要線程同步的實(shí)現(xiàn)了喳坠。

問題的解決:
因此加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調(diào)用宾尚,從而保證了該變量的唯一性和準(zhǔn)確性丙笋。

一共有兩種鎖,來實(shí)現(xiàn)線程同步問題煌贴,分別是:synchronized和ReentrantLock御板。下面我們就帶著上述問題,看看這兩種鎖是如何解決的牛郑。

二怠肋、synchronized關(guān)鍵字

1.synchronized簡介

synchronized實(shí)現(xiàn)同步的基礎(chǔ):Java中每個(gè)對象都可以作為鎖。當(dāng)線程試圖訪問同步代碼時(shí)淹朋,必須先獲得對象鎖笙各,退出或拋出異常時(shí)必須釋放鎖钉答。Synchronzied實(shí)現(xiàn)同步的表現(xiàn)形式分為:代碼塊同步和方法同步。

2.synchronized原理

JVM基于進(jìn)入和退出Monitor對象來實(shí)現(xiàn)代碼塊同步和方法同步杈抢,兩者實(shí)現(xiàn)細(xì)節(jié)不同数尿。

代碼塊同步:在編譯后通過將monitorenter指令插入到同步代碼塊的開始處,將monitorexit指令插入到方法結(jié)束處和異常處惶楼,通過反編譯字節(jié)碼可以觀察到右蹦。任何一個(gè)對象都有一個(gè)monitor與之關(guān)聯(lián),線程執(zhí)行monitorenter指令時(shí)歼捐,會(huì)嘗試獲取對象對應(yīng)的monitor的所有權(quán)何陆,即嘗試獲得對象的鎖。

方法同步:synchronized方法在method_info結(jié)構(gòu)有ACC_synchronized標(biāo)記豹储,線程執(zhí)行時(shí)會(huì)識(shí)別該標(biāo)記贷盲,獲取對應(yīng)的鎖,實(shí)現(xiàn)方法同步剥扣。

兩者雖然實(shí)現(xiàn)細(xì)節(jié)不同巩剖,但本質(zhì)上都是對一個(gè)對象的監(jiān)視器(monitor)的獲取。任意一個(gè)對象都擁有自己的監(jiān)視器朦乏,當(dāng)同步代碼塊或同步方法時(shí)球及,執(zhí)行方法的線程必須先獲得該對象的監(jiān)視器才能進(jìn)入同步塊或同步方法,沒有獲取到監(jiān)視器的線程將會(huì)被阻塞呻疹,并進(jìn)入同步隊(duì)列吃引,狀態(tài)變?yōu)锽LOCKED。當(dāng)成功獲取監(jiān)視器的線程釋放了鎖后刽锤,會(huì)喚醒阻塞在同步隊(duì)列的線程镊尺,使其重新嘗試對監(jiān)視器的獲取。

對象并思、監(jiān)視器庐氮、同步隊(duì)列和執(zhí)行線程間的關(guān)系如下圖:

3.synchronized的使用場景

①方法同步

public synchronized void method1

鎖住的是該對象,類的其中一個(gè)實(shí)例,當(dāng)該對象(僅僅是這一個(gè)對象)在不同線程中執(zhí)行這個(gè)同步方法時(shí)宋彼,線程之間會(huì)形成互斥弄砍。達(dá)到同步效果,但如果不同線程同時(shí)對該類的不同對象執(zhí)行這個(gè)同步方法時(shí)输涕,則線程之間不會(huì)形成互斥音婶,因?yàn)樗麄儞碛械氖遣煌逆i。

②代碼塊同步

synchronized(this){ //TODO }

描述同①

③方法同步

public synchronized static void method3

鎖住的是該類莱坎,當(dāng)所有該類的對象(多個(gè)對象)在不同線程中調(diào)用這個(gè)static同步方法時(shí)衣式,線程之間會(huì)形成互斥,達(dá)到同步效果。

④代碼塊同步

synchronized(Test.class){ //TODO}

同③

⑤代碼塊同步

synchronized(o) {}

這里面的o可以是一個(gè)任何Object對象或數(shù)組碴卧,并不一定是它本身對象或者類弱卡,誰擁有o這個(gè)鎖,誰就能夠操作該塊程序代碼住册。

4.解決線程同步的實(shí)例

針對上述方法婶博,具體的解決方式如下:

public class Ticket implements Runnable {
    // 當(dāng)前擁有的票數(shù)
    private int num = 100;

    public void run() {
        while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
                synchronized (this) {
                    // 輸出賣票信息
                    if(num>0){
                        System.out.println(Thread.currentThread().getName() + ".....sale...." + num--);
                    }
                    
                }
        }
    }
}

輸出部分結(jié)果:

Thread-2.....sale....10
Thread-1.....sale....9
Thread-3.....sale....8
Thread-0.....sale....7
Thread-2.....sale....6
Thread-1.....sale....5
Thread-2.....sale....4
Thread-1.....sale....3
Thread-3.....sale....2
Thread-0.....sale....1

可以看出實(shí)現(xiàn)了線程同步。同時(shí)改了一下邏輯界弧,在進(jìn)入到同步代碼塊時(shí)凡蜻,先判斷現(xiàn)在是否有沒有票搭综,然后再買票垢箕,防止出現(xiàn)沒票還要售出的情況。通過同步代碼塊實(shí)現(xiàn)了線程同步兑巾,其他方法也一樣可以實(shí)現(xiàn)該效果条获。

三、ReentrantLock鎖

1.Lock接口

Lock蒋歌,鎖對象帅掘。在Java中鎖是用來控制多個(gè)線程訪問共享資源的方式,一般來說堂油,一個(gè)鎖能夠防止多個(gè)線程同時(shí)訪問共享資源(但有的鎖可以允許多個(gè)線程并發(fā)訪問共享資源修档,比如讀寫鎖,后面我們會(huì)分析)府框。在Lock接口出現(xiàn)之前吱窝,Java程序是靠synchronized關(guān)鍵字(后面分析)實(shí)現(xiàn)鎖功能的,而JAVA SE5.0之后并發(fā)包中新增了Lock接口用來實(shí)現(xiàn)鎖的功能迫靖,它提供了與synchronized關(guān)鍵字類似的同步功能院峡,只是在使用時(shí)需要顯式地獲取和釋放鎖,缺點(diǎn)就是缺少像synchronized那樣隱式獲取釋放鎖的便捷性系宜,但是卻擁有了鎖獲取與釋放的可操作性照激,可中斷的獲取鎖以及超時(shí)獲取鎖等多種synchronized關(guān)鍵字所不具備的同步特性。

Lock接口的主要方法(還有兩個(gè)方法比較復(fù)雜盹牧,暫不介紹):

**void lock(): **執(zhí)行此方法時(shí), 如果鎖處于空閑狀態(tài), 當(dāng)前線程將獲取到鎖. 相反, 如果鎖已經(jīng)被其他線程持有, 將禁用當(dāng)前線程, 直到當(dāng)前線程獲取到鎖.
boolean tryLock():如果鎖可用, 則獲取鎖, 并立即返回true, 否則返回false. 該方法和lock()的區(qū)別在于, tryLock()只是"試圖"獲取鎖, 如果鎖不可用, 不會(huì)導(dǎo)致當(dāng)前線程被禁用, 當(dāng)前線程仍然繼續(xù)往下執(zhí)行代碼. 而lock()方法則是一定要獲取到鎖, 如果鎖不可用, 就一直等待, 在未獲得鎖之前,當(dāng)前線程并不繼續(xù)向下執(zhí)行. 通常采用如下的代碼形式調(diào)用tryLock()方法:
void unlock():執(zhí)行此方法時(shí), 當(dāng)前線程將釋放持有的鎖. 鎖只能由持有者釋放, 如果線程并不持有鎖, 卻執(zhí)行該方法, 可能導(dǎo)致異常的發(fā)生.
Condition newCondition():條件對象俩垃,獲取等待通知組件。該組件和當(dāng)前的鎖綁定汰寓,當(dāng)前線程只有獲取了鎖口柳,才能調(diào)用該組件的await()方法,而調(diào)用后踩寇,當(dāng)前線程將縮放鎖啄清。

ReentrantLock,一個(gè)可重入的互斥鎖,它具有與使用synchronized方法和語句所訪問的隱式監(jiān)視器鎖相同的一些基本行為和語義辣卒,但功能更強(qiáng)大掷贾。(重入鎖后面介紹)

2.ReentrantLock的使用

關(guān)于ReentrantLock的使用很簡單,只需要顯示調(diào)用荣茫,獲得同步鎖想帅,釋放同步鎖即可。

ReentrantLock lock = new ReentrantLock(); //參數(shù)默認(rèn)false啡莉,不公平鎖    
 .....................    
lock.lock(); //如果被其它資源鎖定港准,會(huì)在此等待鎖釋放,達(dá)到暫停的效果    
try {    
    //操作    
} finally {    
    lock.unlock();  //釋放鎖  
}  
3.解決線程同步的實(shí)例

針對上述方法咧欣,具體的解決方式如下:

public class Ticket implements Runnable {
    // 當(dāng)前擁有的票數(shù)
    private int num = 100;
    ReentrantLock lock = new ReentrantLock();

    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }

            lock.lock();
            // 輸出賣票信息
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + ".....sale...." + num--);
            }
            lock.unlock();

        }
    }
}

四浅缸、重入鎖

當(dāng)一個(gè)線程得到一個(gè)對象后,再次請求該對象鎖時(shí)是可以再次得到該對象的鎖的魄咕。
具體概念就是:自己可以再次獲取自己的內(nèi)部鎖衩椒。
Java里面內(nèi)置鎖(synchronized)和Lock(ReentrantLock)都是可重入的。

public class SynchronizedTest {
    public void method1() {
        synchronized (SynchronizedTest.class) {
            System.out.println("方法1獲得ReentrantTest的鎖運(yùn)行了");
            method2();
        }
    }
    public void method2() {
        synchronized (SynchronizedTest.class) {
            System.out.println("方法1里面調(diào)用的方法2重入鎖,也正常運(yùn)行了");
        }
    }
    public static void main(String[] args) {
        new SynchronizedTest().method1();
    }
}

上面便是synchronized的重入鎖特性哮兰,即調(diào)用method1()方法時(shí)毛萌,已經(jīng)獲得了鎖,此時(shí)內(nèi)部調(diào)用method2()方法時(shí)喝滞,由于本身已經(jīng)具有該鎖阁将,所以可以再次獲取。

public class ReentrantLockTest {
    private Lock lock = new ReentrantLock();
    public void method1() {
        lock.lock();
        try {
            System.out.println("方法1獲得ReentrantLock鎖運(yùn)行了");
            method2();
        } finally {
            lock.unlock();
        }
    }
    public void method2() {
        lock.lock();
        try {
            System.out.println("方法1里面調(diào)用的方法2重入ReentrantLock鎖,也正常運(yùn)行了");
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        new ReentrantLockTest().method1();
    }
}

上面便是ReentrantLock的重入鎖特性右遭,即調(diào)用method1()方法時(shí)做盅,已經(jīng)獲得了鎖,此時(shí)內(nèi)部調(diào)用method2()方法時(shí)狸演,由于本身已經(jīng)具有該鎖言蛇,所以可以再次獲取。

五宵距、公平鎖

CPU在調(diào)度線程的時(shí)候是在等待隊(duì)列里隨機(jī)挑選一個(gè)線程腊尚,由于這種隨機(jī)性所以是無法保證線程先到先得的(synchronized控制的鎖就是這種非公平鎖)。但這樣就會(huì)產(chǎn)生饑餓現(xiàn)象满哪,即有些線程(優(yōu)先級(jí)較低的線程)可能永遠(yuǎn)也無法獲取CPU的執(zhí)行權(quán)婿斥,優(yōu)先級(jí)高的線程會(huì)不斷的強(qiáng)制它的資源。那么如何解決饑餓問題呢哨鸭,這就需要公平鎖了民宿。公平鎖可以保證線程按照時(shí)間的先后順序執(zhí)行,避免饑餓現(xiàn)象的產(chǎn)生像鸡。但公平鎖的效率比較低活鹰,因?yàn)橐獙?shí)現(xiàn)順序執(zhí)行,需要維護(hù)一個(gè)有序隊(duì)列。

ReentrantLock便是一種公平鎖志群,通過在構(gòu)造方法中傳入true就是公平鎖着绷,傳入false,就是非公平鎖锌云。

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

以下是使用公平鎖實(shí)現(xiàn)的效果:

public class LockFairTest implements Runnable{
    //創(chuàng)建公平鎖
    private static ReentrantLock lock=new ReentrantLock(true);
    public void run() {
        while(true){
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName()+"獲得鎖");
            }finally{
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        LockFairTest lft=new LockFairTest();
        Thread th1=new Thread(lft);
        Thread th2=new Thread(lft);
        th1.start();
        th2.start();
    }
}

輸出結(jié)果:

Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖
Thread-1獲得鎖
Thread-0獲得鎖

這是截取的部分執(zhí)行結(jié)果荠医,分析結(jié)果可看出兩個(gè)線程是交替執(zhí)行的,幾乎不會(huì)出現(xiàn)同一個(gè)線程連續(xù)執(zhí)行多次桑涎。

六彬向、synchronized和ReentrantLock的比較

1.區(qū)別:

1)Lock是一個(gè)接口,而synchronized是Java中的關(guān)鍵字攻冷,synchronized是內(nèi)置的語言實(shí)現(xiàn)娃胆;

2)synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線程占有的鎖讲衫,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生缕棵;而Lock在發(fā)生異常時(shí),如果沒有主動(dòng)通過unLock()去釋放鎖涉兽,則很可能造成死鎖現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖篙程;

3)Lock可以讓等待鎖的線程響應(yīng)中斷枷畏,而synchronized卻不行,使用synchronized時(shí)虱饿,等待的線程會(huì)一直等待下去拥诡,不能夠響應(yīng)中斷;

4)通過Lock可以知道有沒有成功獲取鎖氮发,而synchronized卻無法辦到渴肉。

5)Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。

總結(jié):ReentrantLock相比synchronized爽冕,增加了一些高級(jí)的功能仇祭。但也有一定缺陷。
在ReentrantLock類中定義了很多方法颈畸,比如:

isFair()        //判斷鎖是否是公平鎖

isLocked()    //判斷鎖是否被任何線程獲取了

isHeldByCurrentThread()   //判斷鎖是否被當(dāng)前線程獲取了

hasQueuedThreads()   //判斷是否有線程在等待該鎖
2.兩者在鎖的相關(guān)概念上區(qū)別:

1)可中斷鎖
顧名思義乌奇,就是可以相應(yīng)中斷的鎖。

在Java中眯娱,synchronized就不是可中斷鎖礁苗,而Lock是可中斷鎖。如果某一線程A正在執(zhí)行鎖中的代碼徙缴,另一線程B正在等待獲取該鎖试伙,可能由于等待時(shí)間過長,線程B不想等待了,想先處理其他事情疏叨,我們可以讓它中斷自己或者在別的線程中中斷它吱抚,這種就是可中斷鎖。

lockInterruptibly()的用法體現(xiàn)了Lock的可中斷性考廉。

2)公平鎖

公平鎖即盡量以請求鎖的順序來獲取鎖秘豹。比如同是有多個(gè)線程在等待一個(gè)鎖,當(dāng)這個(gè)鎖被釋放時(shí)昌粤,等待時(shí)間最久的線程(最先請求的線程)會(huì)獲得該鎖(并不是絕對的既绕,大體上是這種順序),這種就是公平鎖涮坐。

非公平鎖即無法保證鎖的獲取是按照請求鎖的順序進(jìn)行的凄贩。這樣就可能導(dǎo)致某個(gè)或者一些線程永遠(yuǎn)獲取不到鎖。

在Java中袱讹,synchronized就是非公平鎖疲扎,它無法保證等待的線程獲取鎖的順序。ReentrantLock可以設(shè)置成公平鎖捷雕。

3)讀寫鎖

讀寫鎖將對一個(gè)資源(比如文件)的訪問分成了2個(gè)鎖椒丧,一個(gè)讀鎖和一個(gè)寫鎖。

正因?yàn)橛辛俗x寫鎖救巷,才使得多個(gè)線程之間的讀操作可以并發(fā)進(jìn)行壶熏,不需要同步,而寫操作需要同步進(jìn)行浦译,提高了效率棒假。

ReadWriteLock就是讀寫鎖,它是一個(gè)接口精盅,ReentrantReadWriteLock實(shí)現(xiàn)了這個(gè)接口帽哑。

可以通過readLock()獲取讀鎖,通過writeLock()獲取寫鎖叹俏。

4)綁定多個(gè)條件

一個(gè)ReentrantLock對象可以同時(shí)綁定多個(gè)Condition對象妻枕,而在synchronized中,鎖對象的wait()和notify()或notifyAll()方法可以實(shí)現(xiàn)一個(gè)隱含的條件她肯,如果要和多余一個(gè)條件關(guān)聯(lián)的時(shí)候佳头,就不得不額外地添加一個(gè)鎖,而ReentrantLock則無須這么做晴氨,只需要多次調(diào)用new Condition()方法即可康嘉。

3.性能比較

在性能上來說,如果競爭資源不激烈籽前,兩者的性能是差不多的亭珍,而當(dāng)競爭資源非常激烈時(shí)(即有大量線程同時(shí)競爭)敷钾,此時(shí)ReentrantLock的性能要遠(yuǎn)遠(yuǎn)優(yōu)于synchronized。所以說肄梨,在具體使用時(shí)要根據(jù)適當(dāng)情況選擇阻荒。

在JDK1.5中,synchronized是性能低效的众羡。因?yàn)檫@是一個(gè)重量級(jí)操作侨赡,它對性能最大的影響是阻塞的是實(shí)現(xiàn),掛起線程和恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成粱侣,這些操作給系統(tǒng)的并發(fā)性帶來了很大的壓力羊壹。相比之下使用Java提供的ReentrankLock對象,性能更高一些齐婴。到了JDK1.6油猫,發(fā)生了變化,對synchronize加入了很多優(yōu)化措施柠偶,有自適應(yīng)自旋情妖,鎖消除,鎖粗化诱担,輕量級(jí)鎖毡证,偏向鎖等等。導(dǎo)致在JDK1.6上synchronize的性能并不比Lock差该肴。官方也表示情竹,他們也更支持synchronize,在未來的版本中還有優(yōu)化余地匀哄,所以還是提倡在synchronized能實(shí)現(xiàn)需求的情況下,優(yōu)先考慮使用synchronized來進(jìn)行同步雏蛮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涎嚼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子挑秉,更是在濱河造成了極大的恐慌法梯,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犀概,死亡現(xiàn)場離奇詭異立哑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)姻灶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門铛绰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人产喉,你說我怎么就攤上這事捂掰「一幔” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵这嚣,是天一觀的道長鸥昏。 經(jīng)常有香客問我,道長姐帚,這世上最難降的妖魔是什么吏垮? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮罐旗,結(jié)果婚禮上膳汪,老公的妹妹穿的比我還像新娘。我一直安慰自己尤莺,他們只是感情好旅敷,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颤霎,像睡著了一般媳谁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上友酱,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天晴音,我揣著相機(jī)與錄音,去河邊找鬼缔杉。 笑死锤躁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的或详。 我是一名探鬼主播系羞,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼霸琴!你這毒婦竟也來了椒振?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對情侶失蹤梧乘,失蹤者是張志新(化名)和其女友劉穎澎迎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體选调,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夹供,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仁堪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哮洽。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖枝笨,靈堂內(nèi)的尸體忽然破棺而出袁铐,到底是詐尸還是另有隱情揭蜒,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布剔桨,位于F島的核電站屉更,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏洒缀。R本人自食惡果不足惜瑰谜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望树绩。 院中可真熱鬧萨脑,春花似錦、人聲如沸饺饭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘫俊。三九已至鹊杖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扛芽,已是汗流浹背骂蓖。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留川尖,地道東北人登下。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子肪获,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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