java線程池-ThreadPoolExecutor源碼分析

線程是程序執(zhí)行流的最小單元涡尘,合理的使用線程可以充分利用系統(tǒng)資源忍弛、提高吞吐率以及加快響應(yīng)時(shí)間。然而創(chuàng)建線程的消耗很大考抄,為了節(jié)約系統(tǒng)資源细疚,方便管理和監(jiān)控,我們通常會(huì)使用線程池川梅。java 1.5中引入了線程池框架executors疯兼,給我們提供了一個(gè)開箱即用的線程池實(shí)現(xiàn)然遏。

案例1


public class ExecutorCase {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task());
        }
    }

    private static class Task implements Runnable {

        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
                System.out.println(Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

運(yùn)行結(jié)果為:

pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-1

上面的用例生成了一個(gè)大小為3的線程池,并提交了若干任務(wù)吧彪。查看newFixedThreadPool可見其代碼如下啦鸣,可見實(shí)際上它創(chuàng)建了一個(gè)ThreadPoolExecutor的實(shí)例。其實(shí)上来氧,Executors的各種靜態(tài)方法中诫给,除了與ScheduledExecutorService有關(guān)的都是以不同的參數(shù)創(chuàng)建了一個(gè)ThreadPoolExecutor的實(shí)例。

  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

參數(shù)說明


ThreadPoolExecutor的構(gòu)建函數(shù)為:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

每個(gè)參數(shù)的說明如下:

  • corePoolSize
    線程池中的核心線程數(shù)啦扬,當(dāng)提交一個(gè)任務(wù)時(shí)中狂,線程池創(chuàng)建一個(gè)新線程執(zhí)行任務(wù),直到當(dāng)前線程數(shù)等于corePoolSize扑毡;如果當(dāng)前線程數(shù)為corePoolSize胃榕,繼續(xù)提交的任務(wù)被保存到阻塞隊(duì)列中,等待被執(zhí)行瞄摊。這些線程將一直存活勋又,除非設(shè)置了allowCoreThreadTimeOut

  • maximumPoolSize
    線程池中的最大線程數(shù)。當(dāng)阻塞隊(duì)列已滿但客戶端還繼續(xù)向線程池提交任務(wù)時(shí)换帜,若corePoolSize小于maximumPoolSize楔壤,則會(huì)新建線程加入線程池中

  • keepAliveTime
    線程池中的線程的存活時(shí)間

  • TimeUnit
    keepAliveTime的時(shí)間單位

  • workQueue
    某種blockingQueue的實(shí)現(xiàn),用于在沒有空閑線程時(shí)暫時(shí)存儲(chǔ)需要處理的任務(wù)

  • **threadFactory **
    用于自定義線程的創(chuàng)建惯驼,比如給線程一些標(biāo)識(shí)度比較高的名字蹲嚣。

  • **handler **
    線程池的飽和策略,當(dāng)阻塞隊(duì)列滿了祟牲,且沒有空閑的工作線程隙畜,如果繼續(xù)提交任務(wù),必須進(jìn)行某種處理说贝,默認(rèn)的處理方式是拋出RejectedExecutionException異常议惰。

了解了上面這些參數(shù)的含義,對(duì)Executors中的各種預(yù)定義線程池的特性應(yīng)當(dāng)就會(huì)比較了解了乡恕。

ThreadPoolExecutor的內(nèi)部狀態(tài)


其內(nèi)部狀態(tài)如下言询,用ctl的低29位存儲(chǔ)線程數(shù)量,用高三位存儲(chǔ)線程的運(yùn)行狀態(tài)几颜,CAPACITY 頭三位為0倍试,其他位為1,可以方便的獲取狀態(tài)和線程數(shù)蛋哭。

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }

提交方式


有兩種提交方式

  1. Executor.execute()

  2. ExecutorService.submit()

前者簡單的提交一個(gè)線程县习,后者返回一個(gè)FutureTask,可以獲取提交的計(jì)算結(jié)果。區(qū)別留待后續(xù)討論躁愿。

execute代碼


翻譯一下代碼中的注釋:

  1. 如果線程數(shù)小于corePoolSize叛本,則向線程池中添加一個(gè)新線程。
  2. 否則將其加入等待執(zhí)行隊(duì)列中彤钟,如果加入成功来候,則需要再檢查一下是否需要添加新線程或是線程池狀態(tài)已經(jīng)發(fā)生了變化。
  3. 如果隊(duì)列已滿逸雹,則再嘗試添加新的線程营搅,不過調(diào)用參數(shù)core會(huì)發(fā)生改變,說明不是添加的“核心線程”
   /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

addWorker的實(shí)現(xiàn)

        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&  ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

這部分代碼首先判斷線程池的狀態(tài)梆砸,如果線程池的狀態(tài)值大于或等SHUTDOWN转质,則不處理提交的任務(wù)(有一種特殊情況,就是狀態(tài)處于SHUTDOWN 但是已提交了一個(gè)任務(wù)或是任務(wù)隊(duì)列里還有待完成的隊(duì)列)帖世,直接返回休蟹;然后通過參數(shù)core判斷當(dāng)前需要?jiǎng)?chuàng)建的線程是否為核心線程,如果core為true日矫,且當(dāng)前線程數(shù)小于corePoolSize赂弓,則跳出循環(huán),開始創(chuàng)建新的線程:

boolean workerStarted   = false;
boolean workerAdded = false;
Worker  w       = null;
try {
    w = new Worker( firstTask );
    final Thread t = w.thread;
    if ( t != null )
    {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            /*
             * Recheck while holding lock.
             * Back out on ThreadFactory failure or if
             * shut down before lock acquired.
             */
            int rs = runStateOf( ctl.get() );

            if ( rs < SHUTDOWN ||
                 (rs == SHUTDOWN && firstTask == null) )
            {
                if ( t.isAlive() ) /* precheck that t is startable */
                    throw new IllegalThreadStateException();
                workers.add( w );
                int s = workers.size();
                if ( s > largestPoolSize )
                    largestPoolSize = s;
                workerAdded = true;
            }
        } finally {
            mainLock.unlock();
        }
        if ( workerAdded )
        {
            t.start();
            workerStarted = true;
        }
    }
} finally {
    if ( !workerStarted )
        addWorkerFailed( w );
}
return(workerStarted);

這里的核心邏輯就是生成一個(gè)新Worker哪轿,將其加入到workers中盈魁,并且啟動(dòng)與該worker相關(guān)的線程。

Worker簡介

查看Worker類的源碼缔逛,我們可以發(fā)現(xiàn)其繼承了AbstractQueuedSynchronizer备埃,可以方便的實(shí)現(xiàn)工作線程的中止操作姓惑;并實(shí)現(xiàn)了Runnable褐奴,可以將自身作為一個(gè)任務(wù)在工作線程中執(zhí)行;

   /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

在構(gòu)造方法中于毙,先將state設(shè)置為-1敦冬,并且使用線程工廠創(chuàng)建線程。

   /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

在run方法中唯沮,將所有任務(wù)全部委托給了runWorker脖旱。

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

首先,調(diào)用unlock將AQS標(biāo)志設(shè)置為0表示開始接收中斷介蛉,然后保證在線程池即將停止時(shí)確定線程被中斷萌庆,而沒有被停止時(shí)清除中斷信號(hào)。注意币旧,Thread.interrupted()會(huì)清除中斷標(biāo)志践险。而后,調(diào)用task的run方法,在完成了第一個(gè)任務(wù)后巍虫,會(huì)使用getTask方法從阻塞隊(duì)列中獲取等待的任務(wù)彭则,如果隊(duì)列中沒有任務(wù),getTask方法會(huì)被阻塞并掛起占遥,不會(huì)占用cpu資源:

getTask方法


 private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

注意在getTask方法中俯抖,會(huì)根據(jù)allowCoreThreadTimeOut 以及線程數(shù)與corePoolSize的關(guān)系給timed賦值,循環(huán)的從workQueue中獲取Runnable瓦胎。注意當(dāng)線程數(shù)大于最大線程數(shù)或timed為true且獲取任務(wù)超時(shí)時(shí)芬萍,會(huì)直接返回null,從而使相關(guān)的Worker runTask的線程退出搔啊。

submit的實(shí)現(xiàn)

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

可以看出先將Callable包裝成了FutureTask(實(shí)現(xiàn)了RunnableFuture)然后進(jìn)行execute担忧。其內(nèi)部狀態(tài)為:

    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;

其get方法為:

   public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

可以看到核心為調(diào)用awaitDone方法:

    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

這段代碼的邏輯如下:

  1. 如果主線程被中斷,則拋出中斷異常坯癣;
  2. 判斷FutureTask當(dāng)前的state瓶盛,如果大于COMPLETING,說明任務(wù)已經(jīng)執(zhí)行完成示罗,則直接返回惩猫;
  3. 如果當(dāng)前state等于COMPLETING,說明任務(wù)已經(jīng)執(zhí)行完蚜点,這時(shí)主線程只需通過yield方法讓出cpu資源轧房,等待state變成NORMAL;
  4. 通過WaitNode類封裝當(dāng)前線程WaitNode() { thread = Thread.currentThread(); }绍绘,為了提高效率通過UNSAFE的CAS將自己添加到waiters鏈表奶镶;
  5. 最終通過LockSupport的park或parkNanos掛起線程;

FutureTask的run實(shí)現(xiàn)

  public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

這里首先查看狀態(tài)必須為NEW陪拘,并且通過CAS將當(dāng)前線程設(shè)置為Future的runner厂镇,然后調(diào)用Callable的call方法,并將結(jié)果或異常存儲(chǔ)左刽,使用Unsafe修改state的狀態(tài)捺信,并調(diào)用finishCompletion方法。

  private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }

這里讀取waiters隊(duì)列(注意每次獲取都使用了CAS)欠痴,獲取以后處理隊(duì)列中所有節(jié)點(diǎn)迄靠。還記得在awaitDone中我們調(diào)用LockSupport.parkNanos(this, nanos)LockSupport.parkNanos(this)將線程掛起么?在finishCompletion方法中我們會(huì)調(diào)用LockSupport.unpark(t) 喚醒在等待的線程喇辽。

ScheduledThreadPoolExecutor


ScheduledThreadPoolExecutor使用ScheduledFutureTask作為自己的task掌挚,其run方法中在調(diào)用run后又重新設(shè)置了自己的狀態(tài),而后更新下次運(yùn)行時(shí)間菩咨,將自己重新加入workQueue中吠式。

       public void run() {
            boolean periodic = isPeriodic();
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {
                setNextRunTime();
                reExecutePeriodic(outerTask);
            }
        }

在schedule方法中,組裝了RunnableScheduledFuture舅世,而后調(diào)用delayedExecute。

    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay,
                                           TimeUnit unit) {
        if (callable == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<V> t = decorateTask(callable,
            new ScheduledFutureTask<V>(callable,
                                       triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

delayedExecute中奇徒,調(diào)用了ensurePrestart雏亚,從而使用addWorker添加了工作進(jìn)程:

   private void delayedExecute(RunnableScheduledFuture<?> task) {
        if (isShutdown())
            reject(task);
        else {
            super.getQueue().add(task);
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                ensurePrestart();
        }
    }

而ScheduledThreadPoolExecutor的workQueue是一個(gè)delayQueue,可以在指定時(shí)間后才獲取到隊(duì)列中的元素摩钙,這樣就實(shí)現(xiàn)了定時(shí)反復(fù)運(yùn)行罢低。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胖笛,隨后出現(xiàn)的幾起案子网持,更是在濱河造成了極大的恐慌,老刑警劉巖长踊,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件功舀,死亡現(xiàn)場離奇詭異,居然都是意外死亡身弊,警方通過查閱死者的電腦和手機(jī)辟汰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阱佛,“玉大人帖汞,你說我怎么就攤上這事〈帐酰” “怎么了翩蘸?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長淮逊。 經(jīng)常有香客問我催首,道長,這世上最難降的妖魔是什么泄鹏? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任郎任,我火速辦了婚禮,結(jié)果婚禮上命满,老公的妹妹穿的比我還像新娘涝滴。我一直安慰自己,他們只是感情好胶台,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杂抽,像睡著了一般诈唬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缩麸,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天铸磅,我揣著相機(jī)與錄音,去河邊找鬼。 笑死阅仔,一個(gè)胖子當(dāng)著我的面吹牛吹散,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播八酒,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼空民,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了羞迷?” 一聲冷哼從身側(cè)響起界轩,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎衔瓮,沒想到半個(gè)月后浊猾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡热鞍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年葫慎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薇宠。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幅疼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昼接,到底是詐尸還是另有隱情爽篷,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布慢睡,位于F島的核電站逐工,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏漂辐。R本人自食惡果不足惜泪喊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望髓涯。 院中可真熱鬧袒啼,春花似錦、人聲如沸纬纪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽包各。三九已至摘仅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間问畅,已是汗流浹背娃属。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國打工六荒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人矾端。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓掏击,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秩铆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砚亭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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