并發(fā)七:AQS隊(duì)列同步器實(shí)現(xiàn)分析

AQS

隊(duì)列同步器(AbstractQueuedSynchronizer)簡(jiǎn)稱AQS,是J.U.C同步構(gòu)件的基礎(chǔ),包括ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphor都是基于AQS實(shí)現(xiàn)的。
理解AQS是理解這些同步工具的基礎(chǔ),基于AQS提供的同步語(yǔ)義可以定制各種功能的同步工具。

AQS原理

用一個(gè)int類型的狀態(tài)變量(volatile)state記錄同步狀態(tài),默認(rèn)值是0
用一個(gè)雙向鏈表實(shí)現(xiàn)的隊(duì)列對(duì)線程進(jìn)程進(jìn)行排隊(duì)和調(diào)度

A線程使用compareAndSet(state,0,1)原子設(shè)置state的
值,設(shè)置成功說(shuō)明state當(dāng)前無(wú)其他線程爭(zhēng)用,A線程取鎖的使用權(quán)。

設(shè)置不成功,說(shuō)明B線程對(duì)state的值進(jìn)行了設(shè)置,并且沒(méi)有復(fù)位(state!=0),B線程持有鎖的使用權(quán)(B線程還沒(méi)有釋放鎖)。A線程會(huì)構(gòu)造成一個(gè)Node節(jié)點(diǎn)加入隊(duì)列尾部并掛起。

當(dāng)B線程執(zhí)行完同步操作后,對(duì)state進(jìn)行復(fù)位(state==0),即釋放鎖,然后從隊(duì)列頭開(kāi)始尋找,發(fā)現(xiàn)正在沉睡的A線程,將其喚醒。

Node節(jié)點(diǎn)

static final class Node {
    /**共享模式 */
    static final Node SHARED = new Node();
    /**獨(dú)占模式 */
    static final Node EXCLUSIVE = null;
    /**取消狀態(tài),由于在同步隊(duì)列中等待的線程等待超時(shí)或被中斷,
     * 需要從同步隊(duì)列中取消等待。 
     */
    static final int CANCELLED = 1;
    /**通知狀態(tài),當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)包含的線程需要運(yùn)行(unpark)
     * 當(dāng)前節(jié)點(diǎn)的線程如果釋放了同步狀態(tài)或者被取消,將通知后續(xù)節(jié)點(diǎn)。
     */
    static final int SIGNAL = -1;
    /**條件阻塞狀態(tài),節(jié)點(diǎn)線程等待在Condition上,
     * 當(dāng)其他線程對(duì)Condition調(diào)用了signal()方法后,
     * 該節(jié)點(diǎn)將會(huì)從等待隊(duì)列中轉(zhuǎn)移到同步隊(duì)列中,
     * 加入到對(duì)同步狀態(tài)的獲取中。 
     */
    static final int CONDITION = -2;
    /**傳播狀態(tài),表示當(dāng)前場(chǎng)景下后續(xù)的acquireShared能夠得以執(zhí)行。*/
    static final int PROPAGATE = -3;
    /**節(jié)點(diǎn)的的狀態(tài) 
     * 初始狀態(tài)為0  表示當(dāng)前節(jié)點(diǎn)在sync隊(duì)列中,等待著獲取狀態(tài)。
     */
    volatile int waitStatus;
    /** 前驅(qū)節(jié)點(diǎn) */
    volatile Node prev;
    /** 后繼節(jié)點(diǎn) */
    volatile Node next;
    /**節(jié)點(diǎn)對(duì)應(yīng)的線程,等待獲取同步狀態(tài)的線程。 */
    volatile Thread thread;
    /**下一等待節(jié)點(diǎn)*/
    Node nextWaiter;
    /**是否共享模式 */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    /**獲取前驅(qū)節(jié)點(diǎn) */
    final Node predecessor() throws NullPointerException {}
    Node() {}
    Node(Thread thread, Node mode) {}
    Node(Thread thread, int waitStatus) {}
}

EXCLUSIVE、SHARED是節(jié)點(diǎn)的兩種模式:獨(dú)占模式和共享模式,分別對(duì)應(yīng)獨(dú)占鎖和共享鎖這兩種典型的鎖。
thread就是節(jié)點(diǎn)對(duì)應(yīng)的線程。
waitStatus指節(jié)點(diǎn)狀態(tài):
1.取消狀態(tài)CANCELLED
2.通知狀態(tài)SIGNAL
3.條件阻塞狀態(tài)CONDITION
4.傳播狀態(tài)PROPAGATE

獨(dú)占模式

獨(dú)占鎖也稱排它鎖,一次只允許一個(gè)線程獲取到鎖,鎖未釋放前其他線程無(wú)法獲取到鎖。
Synchronized關(guān)鍵字獲取的內(nèi)置鎖就是一個(gè)獨(dú)占鎖,每次只能一個(gè)線程獲得對(duì)象的監(jiān)視器進(jìn)入臨界區(qū)。

一個(gè)獨(dú)占鎖的例子,然后來(lái)分析AQS是如何實(shí)現(xiàn)線程同步的。

/**
 * Lock是J.U.C中的接口接口,定義了一組體現(xiàn)完整鎖語(yǔ)義的方法。 
 */
public class ExclusiveLock implements Lock {

    private static class Sync extends AbstractQueuedSynchronizer {
            // 用CAS操作設(shè)置同步狀態(tài)State,當(dāng)前線程把state改為1
            // 其他線程便修改不了,可以看成當(dāng)前線程持有了鎖。
        protected boolean tryAcquire(int acquires) {
            if (this.compareAndSetState(0, acquires)) {
                // 將持有線程設(shè)置為當(dāng)前線程
                this.setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        // 釋放同步狀態(tài)
        protected boolean tryRelease(int releases) {// 釋放同步狀態(tài)
            if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            if (this.getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            this.setExclusiveOwnerThread(null);
            this.setState(0);
            return true;
        }
        // 當(dāng)前線程是否持有線程
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
        // 實(shí)例化Condition對(duì)象
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }
    //  隊(duì)列同步器實(shí)例
    private final Sync sync = new Sync();
    // 加鎖,線程請(qǐng)求到鎖則返回,請(qǐng)求不到鎖則阻塞
    public void lock() {
        sync.acquire(1);
    }
    // 非阻塞加鎖,線程請(qǐng)求到鎖返回true,請(qǐng)求不到鎖返回false,不會(huì)阻塞。
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }
    // 解鎖,
    public void unlock() {
        sync.release(1);
    } 
    // 加鎖 響應(yīng)中斷,在獲取鎖的過(guò)程中線程被中斷拋出InterruptedException異常,停止鎖獲取
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    // 非阻塞加鎖 時(shí)間限制
    public boolean tryLock(long time, TimeUnit unit)  throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }
    // 實(shí)例化 Condition
    public Condition newCondition() {
        return sync.newCondition();
    }
}

獨(dú)占模式加鎖流程

// s1
public final void acquire(int arg) {
   if (!tryAcquire(arg) && 
       acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
       selfInterrupt();
}

s1: 調(diào)用tryAcquire(1)加鎖失敗,構(gòu)造一個(gè)獨(dú)占模式(EXCLUSIVE)的Node準(zhǔn)備入列,轉(zhuǎn)入s2。

   // s2
   private Node addWaiter(Node mode) {
       // 用當(dāng)前線程和mode,實(shí)例化Node
       Node node = new Node(Thread.currentThread(), mode);
       Node pred = tail;
       if (pred != null) {// 隊(duì)列不為空
           node.prev = pred;
           if (compareAndSetTail(pred, node)) {// 快速入列,設(shè)置node為新的尾節(jié)點(diǎn)
               pred.next = node;
               return node;
           }
       }
       enq(node);//隊(duì)列為空||CAS尾節(jié)點(diǎn)設(shè)置失敗
       return node;
   }

s2: 如果隊(duì)列已經(jīng)初始化,使用compareAndSetTail以CAS的方式將Node設(shè)置為尾節(jié)點(diǎn),轉(zhuǎn)入s4;如果隊(duì)列為空或者設(shè)置尾節(jié)點(diǎn)失敗,轉(zhuǎn)入s3

   // s3
   private Node enq(final Node node) {
       for (;;) { // 循環(huán)確保入列成功
           Node t = tail;// 尾節(jié)點(diǎn)
           if (t == null) { // 未初始化,將頭、尾節(jié)點(diǎn)都指向空白節(jié)點(diǎn)
               if (compareAndSetHead(new Node()))
                   tail = head;
           } else { // node 入列
               node.prev = t;// node的前驅(qū)指向尾節(jié)點(diǎn)
               if (compareAndSetTail(t, node)) {// 設(shè)置尾節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn)
                   t.next = node;
                   return t;
               }
           }
       }
   }

s3: 如果隊(duì)列未初始化,實(shí)例化一個(gè)空隊(duì)列tail和head都指向一個(gè)空白節(jié)點(diǎn),使用compareAndSetTail以CAS的方式將Node設(shè)置為尾節(jié)點(diǎn),在循環(huán)中確保設(shè)置成功,轉(zhuǎn)入s4

// s4
final boolean acquireQueued(final Node node, int arg) {
       boolean failed = true;
       try {
           boolean interrupted = false;
           for (;;) {
               final Node p = node.predecessor();//前驅(qū)節(jié)點(diǎn)
               if (p == head && tryAcquire(arg)) {//前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)并成功獲取鎖
                   setHead(node);//設(shè)置節(jié)點(diǎn)為頭
                   p.next = null; // help GC 摘除原來(lái)的頭節(jié)點(diǎn)
                   failed = false;
                   return interrupted;
               } // 前驅(qū)非頭節(jié)點(diǎn)或者重試獲取鎖失敗
               // 線程在此處被掛起,當(dāng)線程被喚醒后也會(huì)在這里重新進(jìn)入for(;;)獲取鎖
               if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                   interrupted = true;
           }
       } finally {
           if (failed)
               cancelAcquire(node);
       }
   }

s4: 如果Node節(jié)點(diǎn)在隊(duì)列中拍第二,重試獲取鎖,獲取成功后,將Node設(shè)置為頭節(jié)點(diǎn)直接返回;否則進(jìn)入s5

s5返回了true說(shuō)明當(dāng)前節(jié)點(diǎn)可掛起了,調(diào)用parkAndCheckInterrupt()方法將線程掛起,線程掛起操作和CAS操作一樣都是調(diào)用Unsafe中的native方法。
如果此線程被中斷了,他會(huì)被喚醒并且返回中斷標(biāo)識(shí)true,進(jìn)入到下次循環(huán),如果拿不到鎖還是接著park,如果拿到鎖返回到s1,會(huì)記錄下中斷狀態(tài)selfInterrupt(),用戶可以自行處理中斷狀態(tài),對(duì)流程沒(méi)有任何影響。

for(;;)主要是為了保障兩點(diǎn):
一是每個(gè)Node掛起前都能將前驅(qū)節(jié)點(diǎn)的狀態(tài)設(shè)置為SIGNAL
二是在每個(gè)Node被喚醒后再次進(jìn)入鎖的獲取中

循環(huán)中如果出現(xiàn)異常,將取消鎖獲取 cancelAcquire(node)。

  // s5
  private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus; // 前驅(qū)節(jié)點(diǎn)狀態(tài)
        if (ws == Node.SIGNAL)//狀態(tài)為SIGNAL,說(shuō)明node可掛起,返回true
            return true;
        if (ws > 0) {//摘掉狀態(tài)為CANCELLED的前驅(qū)節(jié)點(diǎn)
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;// 找到非CANCELLED狀態(tài)的節(jié)點(diǎn),將Node掛在其后面
        } else {//為-3、-2
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);// 設(shè)置前驅(qū)節(jié)點(diǎn)為SIGNAL狀態(tài)
        }
        return false;
    }

s5: 此處主要是將前驅(qū)節(jié)點(diǎn)的waitStatus設(shè)置為SIGNAL。

如果前驅(qū)節(jié)點(diǎn)的狀態(tài)為SIGNAL表示前驅(qū)節(jié)點(diǎn)為"通知狀態(tài)",前驅(qū)節(jié)點(diǎn)釋放鎖后會(huì)后繼等待的節(jié)點(diǎn)會(huì)被喚醒,所以Node可以放心的掛起,直接返回true至s4 進(jìn)行線程掛起。

如果前驅(qū)節(jié)點(diǎn)的狀態(tài)為CANCELLED表示前驅(qū)節(jié)點(diǎn)放棄了鎖獲取,通過(guò)循環(huán)向前查找到,直到找到最近一個(gè)非CANCELLED狀態(tài)的節(jié)點(diǎn),將node掛在它的后邊,CANCELLED節(jié)點(diǎn)會(huì)被從隊(duì)列中摘除。

CAS設(shè)置前驅(qū)節(jié)點(diǎn)的狀態(tài)為SIGNAL,返回s4 false至s4,進(jìn)入下次循環(huán),下次循環(huán)發(fā)現(xiàn)前驅(qū)節(jié)點(diǎn)的狀態(tài)已經(jīng)為SIGNAL了,可以掛起了。

獨(dú)占模式解鎖流程

// s1
public final boolean release(int arg) {
   if (tryRelease(arg)) {//解鎖成功
       Node h = head;
       if (h != null && h.waitStatus != 0)//頭節(jié)點(diǎn)不為空&&不在進(jìn)行中
           unparkSuccessor(h);//喚醒后續(xù)節(jié)點(diǎn)
       return true;
   }
   return false;
}

s1:調(diào)用tryRelease(arg)進(jìn)行解鎖邏輯,解鎖成功,準(zhǔn)備喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
h.waitStatus ==0說(shuō)明后繼節(jié)點(diǎn)正在喚醒中。
轉(zhuǎn)入s2。

// s2
private void unparkSuccessor(Node node) {
       int ws = node.waitStatus;
       if (ws < 0)// head節(jié)點(diǎn)狀態(tài)設(shè)置為0
           compareAndSetWaitStatus(node, ws, 0);
       Node s = node.next;
       if (s == null || s.waitStatus > 0) {// s為空或CANCELLED
           s = null;
           // 從尾部開(kāi)始向前查找,找到一個(gè)非CANCELLED狀態(tài)的節(jié)點(diǎn)
           for (Node t = tail; t != null && t != node; t = t.prev)
               if (t.waitStatus <= 0)
                   s = t;// 賦給s
       }
       if (s != null)// 喚醒s
           LockSupport.unpark(s.thread);
   }

s2:將head節(jié)點(diǎn)的狀態(tài)設(shè)置為0,表示有節(jié)點(diǎn)正在被喚醒。
如果后繼節(jié)點(diǎn)為空或者CANCELLED,從尾節(jié)點(diǎn)開(kāi)始向前查找,找到一個(gè)非CANCELLED狀態(tài)的節(jié)點(diǎn),將其賦值給s。
喚醒s節(jié)點(diǎn),調(diào)用Unsafe中的unpark方法。
需要注意的是被喚醒的節(jié)點(diǎn)還要回到acquireQueued()方法里的掛起點(diǎn),再次進(jìn)行鎖獲取,如果還是沒(méi)有獲取到鎖則接著被掛起。

共享模式

共享鎖是可以有多個(gè)線程獲取并持有的鎖,獲取到鎖的線程都可以進(jìn)入同步代碼塊執(zhí)行。
這有點(diǎn)像數(shù)據(jù)庫(kù)連接池,操作數(shù)據(jù)庫(kù)時(shí)先從池中借出一個(gè)連接,操作完畢,將連接歸還入池,供后續(xù)操作繼續(xù)借用。

AQS共享鎖實(shí)現(xiàn):

public class ShareLock implements Lock {
    // 同步器
    private static class Sync extends AbstractQueuedSynchronizer {
        // 初始化,available:共享許可數(shù)
        Sync(int available) {
            this.setState(available);
        }
        // 獲取同步狀態(tài)
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                //當(dāng)前許可數(shù)
                int available = this.getState();
                //減去1后的剩余量
                int remaining = available - acquires;
                //剩余量<0,直接返回
                if (remaining < 0 
                    //說(shuō)明還有剩余量CAS設(shè)置available
                    || compareAndSetState(available, remaining)) {
                    return remaining;
                }
            }
        }
        // 釋放同步狀態(tài)
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                //當(dāng)前許可數(shù)量
                int current = this.getState();
                //加1后的最新許可量
                int available = current + releases;
                if (available < current) // overflow
                    throw new Error("釋放不合法");
                if (compareAndSetState(current, available))
                    return true;
            }
        }
    }
    // 同步器實(shí)例
    private final Sync sync = new Sync(2);
    // 加鎖
    public void lock() {
        sync.acquireShared(1);
    }
    // 非阻塞加鎖
    public boolean tryLock() {
        return sync.tryAcquireShared(1) > 0;
    }
    // 解鎖
    public void unlock() {
        sync.releaseShared(1);
    }
    // 加鎖 響應(yīng)中斷
    public void lockInterruptibly() 
                            throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    // 非阻塞加鎖 時(shí)間限制
    public boolean tryLock(long time, TimeUnit unit) 
                                throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(time));
    }
    // 實(shí)例化 Condition
    public Condition newCondition() {
        return null;
    }
}

共享模式實(shí)現(xiàn)邏輯

1:初始化同步器Sync時(shí),先設(shè)定許可共享數(shù),即有多少把鎖,許可共享數(shù)保存在共享狀態(tài)state中。

2:每次加鎖許可共享數(shù)都會(huì)減1作為剩余量,當(dāng)剩余量小于0時(shí),說(shuō)明沒(méi)有可用的許可了,直接返回剩余量,AQS中的“acquireShared”發(fā)現(xiàn)剩余量小于0,開(kāi)始構(gòu)造Node進(jìn)入排隊(duì)邏輯。
當(dāng)還有剩余量(remaining>=0)說(shuō)明線程還能獲取共享鎖,剩余量減1,直接返回,取鎖成功。
3:釋放鎖時(shí)將剩余量加1,CAS設(shè)置state為剩余量,設(shè)置成功則釋放鎖成功。
4:獨(dú)占鎖釋放時(shí)沒(méi)有使用CAS操作,因?yàn)楠?dú)占鎖釋放不存在線程爭(zhēng)用,共享鎖會(huì)出現(xiàn)多個(gè)線程釋放鎖的情況,state存在爭(zhēng)用。:
5:共享鎖的newCondition()方法返回null,因?yàn)镃ondition只能使用在獨(dú)占鎖中。后面會(huì)專門分析條件隊(duì)列Condition。
6:理解了獨(dú)占鎖的加鎖解鎖流程,再看共享鎖的加解鎖流程,應(yīng)該沒(méi)有障礙,這里不再累述。
7:需要注意共享鎖在喚醒的節(jié)點(diǎn)后,如發(fā)現(xiàn)還有剩余量,還有節(jié)點(diǎn)在排隊(duì),將繼續(xù)喚醒后繼節(jié)。

小結(jié)

  1. AQS是J.U.C最核心的同步構(gòu)件,也是J.U.C中最難弄懂的類之一,是理解重入鎖、讀寫(xiě)鎖及J.U.C中其他同步工具的基礎(chǔ)。
  2. AQS提供了基礎(chǔ)的同步語(yǔ)義,可以根據(jù)應(yīng)用場(chǎng)景創(chuàng)建不同的同步組件。
  3. 基于AQS實(shí)現(xiàn)的鎖,并非有些文檔說(shuō)的自旋鎖,底層是使用Unsafe的park和unpark方法來(lái)掛起和喚醒線程。

碼字不易,轉(zhuǎn)載請(qǐng)保留原文連接http://www.lxweimin.com/p/d291a6a1879c

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,653評(píng)論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,063評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,835評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,235評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,459評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,819評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,004評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,257評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,676評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,937評(píng)論 1 288
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,717評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,003評(píng)論 2 374

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