鎖的優(yōu)化及注意事項(xiàng)(二)

一崎页、無(wú)鎖

對(duì)于并發(fā)控制而言,鎖是一種悲觀的策略腰埂,總是假設(shè)每一次進(jìn)入臨界區(qū)操作都會(huì)產(chǎn)生沖突飒焦,如果多線程訪問臨界區(qū)資源,就寧可犧牲性能讓線程等待屿笼,所以鎖會(huì)阻塞線程執(zhí)行牺荠。而無(wú)鎖是一種樂觀策略,它會(huì)假設(shè)對(duì)臨界區(qū)資源的訪問是沒有沖突的驴一,所有線程都會(huì)不停頓的執(zhí)行休雌,但是如果遇到?jīng)_突那該怎么辦?無(wú)鎖策略使用一種叫做比較交換的技術(shù)(CAS Compare And Swap)來(lái)鑒定線程沖突肝断,一旦檢測(cè)到?jīng)_突杈曲,就進(jìn)行重試直至沒有沖突為止。

1.1 比較交換(CAS)

使用無(wú)鎖的方式?jīng)]有鎖競(jìng)爭(zhēng)帶來(lái)的系統(tǒng)開銷胸懈,也沒有線程間上下文頻繁調(diào)度帶來(lái)的開銷担扑,相對(duì)有鎖而言具有優(yōu)越的性能。

CAS算法的過(guò)程是這樣的:它包含三個(gè)參數(shù)CAS(V,E,N)趣钱。V表示要更新的變量涌献,E表示預(yù)期值,N表示新值首有。僅當(dāng)V值等于E時(shí)燕垃,才會(huì)將V的值設(shè)為N,如果V值和E值不同绞灼,則說(shuō)明已經(jīng)有其他線程做了更新利术,則當(dāng)前線程什么都不做。最后低矮,CAS返回當(dāng)前V的真實(shí)值印叁。CAS操作是抱著樂觀的態(tài)度進(jìn)行的,它總認(rèn)為自己可以完成操作军掂。當(dāng)多個(gè)線程同時(shí)使用CAS操作一個(gè)變量時(shí)轮蜕,只有一個(gè)會(huì)勝出,并成功更新蝗锥,其余均會(huì)失敗跃洛。失敗的線程不會(huì)被掛起,僅是被告知失敗终议,并且允許再次嘗試汇竭,當(dāng)然也允許失敗的線程放棄操作葱蝗。基于這樣的原理细燎,CAS操作即使沒有鎖两曼,也可以發(fā)現(xiàn)其他線程對(duì)當(dāng)前線程的干擾,并進(jìn)行恰當(dāng)?shù)奶幚怼?/p>

1.2 無(wú)鎖的線程安全整數(shù)

JDK并發(fā)包中有一個(gè)atomic包玻驻,里面實(shí)現(xiàn)了一些直接使用CAS操作的線程安全的類型悼凑。其中,最常用的一個(gè)類璧瞬,應(yīng)該就是AtomicInteger户辫。可以把它看做是一個(gè)整數(shù)嗤锉。但是與Integer不同渔欢,它是可變的,并且是線程安全的瘟忱。對(duì)其進(jìn)行任何修改等操作膘茎,都是用CAS指令進(jìn)行的。AtomicInteger的一些主要方法:

//獲取當(dāng)前的值
public final int get()
//設(shè)置當(dāng)前值
public final void set(int newValue)
//取當(dāng)前的值酷誓,并設(shè)置新的值
public final int getAndSet(int newValue)
//如果當(dāng)前值為expect,則設(shè)置為update
public final boolean compareAndSet(int expect,int update)
//獲取當(dāng)前的值披坏,并自增
public final int getAndIncrement()
//獲取當(dāng)前的值,并自減
public final int getAndDecrement()
//,返回當(dāng)前值盐数,并增加delta
public final int addAndGet(int delta)
//當(dāng)前值+1,返回新值
public final int incrementAndGet()
//當(dāng)前值-1,返回新值
public final int decrementAndGet()

AtomicInteger有兩個(gè)核心字段:

//當(dāng)前實(shí)際取值
private volatile int value;
//value字段在AtomicInteger對(duì)象中的偏移量
private static final long valueOffset;

AtomicInteger使用的具體示例如下:

public class AtomicIntegerDemo {
    static AtomicInteger i = new AtomicInteger();
    public static class AddThred implements Runnable {
        @Override
        public void run() {
            for(int k=0; k<10000; k++) {
                i.incrementAndGet();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread[] ts = new Thread[10];
        for (int k = 0; k < 10; k++) {
            ts[k] = new Thread(new AddThred());
        }
        for(int k=0; k<10; k++) {ts[k].start();}
        for(int k=0; k<10; k++) {ts[k].join();}
        System.out.println(i);
    }
}

如果執(zhí)行這段代碼棒拂,程序輸出100000。說(shuō)明程序正常執(zhí)行玫氢,沒有錯(cuò)誤帚屉。如果不是線程安全的,i的值應(yīng)該會(huì)小于100000漾峡。

incrementAndGet()的內(nèi)部實(shí)現(xiàn)為:

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

第2行的死循環(huán)是因?yàn)镃AS操作未必是成功的攻旦,因此對(duì)于不成功的情況,我們就需要進(jìn)行不斷的嘗試生逸。第3行的get()取得當(dāng)前的值牢屋,接著加1后得到新值next。這樣槽袄,得到了CAS必需的兩個(gè)參數(shù):期望值以及新值烙无。使用compareAndSet()方法將新值next寫入,成功的條件是在寫入的時(shí)刻遍尺,當(dāng)前的值應(yīng)該要等于剛剛?cè)〉玫腸urrent截酷。如果不是這樣,說(shuō)明AtomicInteger的值在第3行到第5行代碼之間乾戏,又被其他線程修改了迂苛。當(dāng)前線程看到的狀態(tài)就是一個(gè)過(guò)期狀態(tài)三热。因此,compareAndSet返回失敗三幻,需要下一次重試康铭,直到成功。
和AtomicInteger類似的類還有AtomicLong用來(lái)代表long型赌髓,AtomicBoolean表示boolean型,AtomicReference表示對(duì)象引用催跪。

1.3 無(wú)鎖的對(duì)象引用:AtomicReference

AtomicReference是對(duì)普通的對(duì)象引用锁蠕,也就是它可以保證你在修改對(duì)象引用時(shí)的線程安全性。線程判斷被修改對(duì)象是否可以正確寫入的條件是對(duì)象的當(dāng)前值和期望值是否一致懊蒸。但有可能出現(xiàn)例外荣倾,當(dāng)獲得對(duì)象當(dāng)前數(shù)據(jù)后,對(duì)象的值又恢復(fù)為舊值骑丸。這樣舌仍,當(dāng)前線程就無(wú)法正確判斷這個(gè)對(duì)象究竟是否被修改過(guò)。有這樣一個(gè)案例:打一個(gè)比方通危,如果有一家蛋糕店铸豁,為了挽留客戶,決定為貴賓卡里余額小于20元的客戶一次性贈(zèng)送20元菊碟。但條件是节芥,每一位客戶只能被贈(zèng)送一次。

定義用戶賬戶余額:

static AtomicReference<Integer> money = new AtomicReference<Integer>();
money.set(19);

接著逆害,需要若干個(gè)后臺(tái)線程头镊,它們不斷掃描數(shù)據(jù),并為滿足條件的客戶充值:

for(int i=0; i<3; i++) {
    new Thread() {
        public void run() {
            while(true) {
                while(true) {
                    Integer m = money.get();
                    if(m<20) {
                        if(money.compareAndSet(m, m+20)) {
                            System.out.println("余額小于20元魄幕,充值成功相艇,余額:"+ money.get() + "元");
                            break;
                        } else {
                            break;
                        }
                    }
                }
            }
        };
    }.start();

此時(shí),如果很不幸纯陨,就在贈(zèng)予金額到賬的同時(shí)坛芽,用戶進(jìn)行了一次消費(fèi),正好消費(fèi)20元翼抠,這時(shí)的金額又小于20元靡馁。這時(shí),后臺(tái)的贈(zèng)予就會(huì)誤以為這個(gè)賬戶還沒有贈(zèng)予机久。所以臭墨,存在多次被贈(zèng)予的可能。下面模擬了這個(gè)消費(fèi)線程:

new Thread() {
    public void run() {
        for (int j = 0; j < 100; j++) {
            while(true) {
                Integer m = money.get();
                if(m>10) {
                    System.out.println("大于10元");
                    if(money.compareAndSet(m, m-10)) {
                        System.out.println("成功消費(fèi)10元膘盖,余額:" + money.get());
                        break;
                    }
                } else {
                    System.out.println("沒有足夠金額");
                    break;
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {

            }
        }
    };
}.start();

上述代碼胧弛,消費(fèi)者只要貴賓卡里的錢大于10元尤误,就會(huì)立即進(jìn)行一次10元的消費(fèi)。執(zhí)行上述代碼结缚,得到的輸出如下:

余額小于20元损晤,充值成功,余額:39元
大于10元
成功消費(fèi)10元红竭,余額:29
大于10元
成功消費(fèi)10元尤勋,余額:19
余額小于20元,充值成功茵宪,余額:39元
大于10元
成功消費(fèi)10元最冰,余額:29
大于10元
成功消費(fèi)10元,余額:39
余額小于20元稀火,充值成功暖哨,余額:39元

可以看到,這個(gè)賬戶被先后反復(fù)多次充值凰狞。其原因正是因?yàn)橘~戶余額被反復(fù)修改篇裁,修改后的值等于原有的值,使得CAS操作無(wú)法正確判斷當(dāng)前數(shù)據(jù)狀態(tài)赡若。

1.4 帶有時(shí)間戳的隊(duì)形引用:AtomicStampedReference

之所以出現(xiàn)上面的這種情況达布,是因?yàn)锳tomicReference在對(duì)象修改過(guò)程中,丟失了狀態(tài)信息逾冬。AtomicStampedReference則解決了上述問題往枣,其內(nèi)部不僅維護(hù)了對(duì)象值,還維護(hù)了一個(gè)時(shí)間戳粉渠。當(dāng)AtomicStampedReference對(duì)應(yīng)的數(shù)值被修改時(shí)分冈,除了更新數(shù)據(jù)本身外,還必須要更新時(shí)間戳霸株。當(dāng)AtomicStampedReference設(shè)置對(duì)象值時(shí)雕沉,對(duì)象值以及時(shí)間戳都必須滿足期望值,寫入才會(huì)成功去件。因此坡椒,即使對(duì)象值被反復(fù)讀寫,寫回原值尤溜,只要時(shí)間戳發(fā)生變化倔叼,就能防止不恰當(dāng)?shù)膶懭搿?br> AtomicStampedReference的幾個(gè)API在AtomicReference的基礎(chǔ)上新增了有關(guān)時(shí)間戳的信息:

//比較設(shè)置參數(shù)依次為:期望值  寫入新值  期望時(shí)間戳  新時(shí)間戳
public boolean compareAndSet(V expectedReference, V newReference,int expectedStamp,int newStamp)
//獲得當(dāng)前對(duì)象引用
public V getReference()
//獲得當(dāng)前時(shí)間戳
public int getStamp()
//設(shè)置當(dāng)前對(duì)象引用和時(shí)間戳
public void set(V newReference, int newStamp)

我們使用AtomicStampedReference解決上面反復(fù)充值的問題:

public class AtomicStampedReferenceDemo {
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19,0);
    public static void main(String[] args) {
        for(int i=0; i<3; i++) {
            final int timestamp = money.getStamp();
            new Thread() {
                public void run() {
                    while(true) {
                        while(true) {
                            Integer m = money.getReference();
                            if(m<20) {
                                if(money.compareAndSet(m, m+20,timestamp,timestamp+1)) {
                                    System.out.println("余額小于20元,充值成功宫莱,余額:"+ money.getReference() + "元");
                                    break;
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                };
            }.start();
        }
        new Thread() {
            public void run() {
                for (int j = 0; j < 100; j++) {
                    while(true) {
                        int timestamp = money.getStamp();
                        Integer m = money.getReference();
                        if(m>10) {
                            System.out.println("大于10元");
                            if(money.compareAndSet(m, m-10,timestamp,timestamp+1)) {
                                System.out.println("成功消費(fèi)10元丈攒,余額:" + money.getReference());
                                break;
                            }
                        } else {
                            System.out.println("沒有足夠金額");
                            break;
                        }
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO: handle exception
                    }
                }
            };
        }.start();
    }
}

結(jié)果輸出如下:

余額小于20元,充值成功,余額:39元
大于10元
成功消費(fèi)10元巡验,余額:29
大于10元
成功消費(fèi)10元际插,余額:19
大于10元
成功消費(fèi)10元,余額:9
沒有足夠金額

1.5 數(shù)組也能無(wú)鎖:AtomicIntegerArray

當(dāng)前可用的原子數(shù)組有:AtomicIntegerArray显设、AtomicLongArray框弛、AtomicReferenceArray,分別表示整數(shù)數(shù)組捕捂、long型數(shù)組和普通對(duì)象數(shù)組瑟枫。以AtomicIntegerArray為例,展示原子數(shù)組的使用方式指攒。AtomicIntegerArray本質(zhì)上是對(duì)int[]類型的封裝慷妙,使用Unsafe類通過(guò)CAS的方式控制int[]在多線程的安全性。它提供以下幾個(gè)核心API:

//獲得數(shù)組第i個(gè)下標(biāo)的元素
public final int get(int i)
//獲得數(shù)組的長(zhǎng)度
public final int length()
//將數(shù)組第i個(gè)下標(biāo)設(shè)置為newValue幽七,并返回舊的值
public final int getAndSet(int i, int newValue)
 //進(jìn)行CAS操作,如果第i個(gè)下標(biāo)的元素等于expect溅呢,則設(shè)置為update澡屡,設(shè)置成功返回true
 public final boolean compareAndSet(int i, int expect, int update)
 //將第i個(gè)下標(biāo)元素加 1
 public final int getAndIncrement(int i)
 //將第i個(gè)下標(biāo)的元素減1
 public final int getAndDecrement(int i)
 //將第i個(gè)下標(biāo)的元素增加delta(delta可以是負(fù)數(shù))
 public final int getAndAdd(int i, int delta)

下面展示AtomicIntegerArray的使用:

public class AtomicIntegerArrayDemo {
    static AtomicIntegerArray arr = new AtomicIntegerArray(10);
    public static class AddThread implements Runnable {
        @Override
        public void run() {
            for(int k=0; k<10000; k++) {
                arr.getAndIncrement(k%arr.length());
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread[] ts = new Thread[10];
        for(int k=0; k<10; k++) {
            ts[k] = new Thread(new AddThread());
        }
        for(int k=0; k<10; k++) {
            ts[k].start();
        }
        for(int k=0; k<10; k++) {
            ts[k].join();
        }
        System.out.println(arr);
    }
}

結(jié)果為:

[10000,10000,10000,10000,10000,10000,10000,10000,10000,10000]

如果線程安全,數(shù)組內(nèi)10個(gè)元素的值必然都是10000咐旧。反之驶鹉,如果線程不安全,則部分或者全部數(shù)值會(huì)小于10000铣墨。

1.6 讓普通變量也享受原子操作:AtomicIntegerFieldUpdater

可能由于初期考慮不周室埋,一些普通變量會(huì)有線程安全的需求,原子包中的一個(gè)實(shí)用工具類AtomicIntegerFieldUpdater可以讓普通變量享受CAS原子操作帶來(lái)的線程安全性伊约。根據(jù)類型的不同姚淆,這個(gè)Updater有三種:AtomicIntegerFieldUpdater,AtomicLongFieldUpdaterAtomicReferenceFieldUpdater。分別可以對(duì)int,long和普通對(duì)象進(jìn)行CAS修改屡律。

現(xiàn)在思考一個(gè)場(chǎng)景腌逢。假設(shè)某地要進(jìn)行一次選舉。現(xiàn)在模擬這個(gè)投票場(chǎng)景超埋,如果選民投了候選人一票搏讶,就記為1,否則記為0霍殴。最終的選票顯然就是所有數(shù)據(jù)的簡(jiǎn)單求和媒惕。

public class AtomicIntegerFieldUpdaterDemo {
    public static class Candidate {
        int id;
        volatile int score;
    }
    public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater 
        = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
    public static AtomicInteger allScore = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        final Candidate stu = new Candidate();
        Thread[] t = new Thread[10000];
        for(int i=0; i<10000; i++) {
            t[i] = new Thread() {
                @Override
                public void run() {
                    if(Math.random() > 0.4) {
                        scoreUpdater.incrementAndGet(stu);
                        allScore.incrementAndGet();
                    }
                }
            };
            t[i].start();
        }
        for(int i=0; i<10000; i++) {t[i].join();}
        System.out.println("score=" + stu.score);
        System.out.println("allScore=" + allScore);
    }
}

運(yùn)行這段程序,最終的Candidate.score總是和allScore絕對(duì)相等来庭。這說(shuō)明AtomicIntegerFieldUpdater很好地保證了Candidate.score的線程安全妒蔚。

注意:

  1. Updater只能修改它可見范圍的變量。因?yàn)閁pdater是通過(guò)反射得到的這個(gè)變量。如果變量不可見面睛,會(huì)出錯(cuò)絮蒿。比如score申明為private,就不行;
  2. 為了確保變量被正確的讀取叁鉴,必須是volatile修飾土涝;
  3. 由于CAS操作會(huì)通過(guò)對(duì)象實(shí)例中的偏移量直接進(jìn)行賦值,因此幌墓,它不支持static字段(public native long objectFieldOffset()不支持靜態(tài)變量) 但壮。

二、有關(guān)死鎖的問題

死鎖:兩個(gè)或者多個(gè)線程常侣,相互占用對(duì)方需要的資源蜡饵,而都不進(jìn)行釋放,導(dǎo)致彼此之間都相互等待對(duì)方釋放資源胳施,產(chǎn)生了無(wú)限制等待的現(xiàn)象溯祸。死鎖一旦發(fā)生,如果沒有外力接入舞肆,這種等待將永遠(yuǎn)存在焦辅,從而對(duì)線程產(chǎn)生嚴(yán)重的影響。

下面用一個(gè)簡(jiǎn)單的例子模擬死鎖問題:

public class DeadLock extends Thread {
    protected Object tool;
    static Object fork1 = new Object();
    static Object fork2 = new Object();
    public DeadLock(Object obj) {
        this.tool = obj;
        if(tool == fork1) {
            this.setName("A");
        }
        if(tool == fork2) {
            this.setName("B");
        }
    }
    @Override
    public void run() {
        if(tool == fork1) {
            synchronized (fork1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (fork2) {
                    System.out.println("哲學(xué)家A開始吃飯了");
                }
            }
        }
        if(tool == fork2) {
            synchronized (fork2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (fork1) {
                    System.out.println("哲學(xué)家B開始吃飯了");
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        DeadLock A = new DeadLock(fork1);
        DeadLock B = new DeadLock(fork2);
        A.start();
        B.start();
        Thread.sleep(1000);
    }
}

上述代碼模擬了兩個(gè)哲學(xué)家互相等待對(duì)方的叉子椿胯。如果吃飯必須用兩個(gè)叉子筷登,哲學(xué)家A先占用叉子1,哲學(xué)家B占用叉子2哩盲,接著他們就互相等待前方,都沒有辦法獲得兩個(gè)叉子用餐,這就是死鎖廉油。

如果在實(shí)際環(huán)境中惠险,遇到了這種情況,通常的表現(xiàn)就是相關(guān)的進(jìn)程不再工作抒线,并且CPU占用率為0(因?yàn)樗梨i的線程不占用CPU)莺匠。想確認(rèn)問題,需要使用JDK提供的工具十兢。首先趣竣,可以使用jps命令得到j(luò)ava進(jìn)程的進(jìn)程ID,接著使用jstack命令得到線程的線程堆棧:

jps

獲取進(jìn)程旱物。

jstack [id]

查看指定進(jìn)程的堆棧信息遥缕。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宵呛,隨后出現(xiàn)的幾起案子单匣,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户秤,死亡現(xiàn)場(chǎng)離奇詭異码秉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鸡号,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門转砖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鲸伴,你說(shuō)我怎么就攤上這事府蔗。” “怎么了汞窗?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵姓赤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我仲吏,道長(zhǎng)不铆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任裹唆,我火速辦了婚禮誓斥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘品腹。我一直安慰自己岖食,他們只是感情好红碑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布舞吭。 她就那樣靜靜地躺著,像睡著了一般析珊。 火紅的嫁衣襯著肌膚如雪羡鸥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天忠寻,我揣著相機(jī)與錄音惧浴,去河邊找鬼。 笑死奕剃,一個(gè)胖子當(dāng)著我的面吹牛衷旅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纵朋,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼柿顶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了操软?” 一聲冷哼從身側(cè)響起嘁锯,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后家乘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝗羊,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年仁锯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了耀找。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扑馁,死狀恐怖涯呻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腻要,我是刑警寧澤复罐,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站雄家,受9級(jí)特大地震影響效诅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜趟济,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一乱投、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顷编,春花似錦戚炫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至钮惠,卻和暖如春茅糜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背素挽。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蔑赘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人预明。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓缩赛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親撰糠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酥馍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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