近萬(wàn)字宵蕉,就是為了和你聊聊線程池

更多精彩請(qǐng)關(guān)注公眾號(hào)xhJaver,京東java工程師和你一起成長(zhǎng)

我們知道,在計(jì)算機(jī)中創(chuàng)建一個(gè)線程和銷(xiāo)毀一個(gè)線程都是十分耗費(fèi)資源的操作节榜,有一種思想叫做羡玛,池化思想,就是說(shuō)我們創(chuàng)建個(gè)池子宗苍,把耗費(fèi)資源的操作都提前做好稼稿,后面大家一起用創(chuàng)建好的東西,最后統(tǒng)一銷(xiāo)毀讳窟。省去了用一次創(chuàng)建一次让歼,銷(xiāo)毀一次,這種耗費(fèi)資源的操作挪钓。

一是越、線程池工作原理

線程池就是這種思想耳舅,他的基本工作流程如下圖所示

線程池原理流程圖.png

那么他的核心線程碌上,任務(wù)隊(duì)列倚评,這些又是什么呢?怎么設(shè)置呢馏予?

這些就要從代碼入手了天梧,我們先來(lái)看下線程池構(gòu)造方法的代碼

二、線程池構(gòu)造方法

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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

其實(shí)ThreadPoolExecutor有四種構(gòu)造方法霞丧,不過(guò)底層都是用這個(gè)7個(gè)參數(shù)的構(gòu)造方法呢岗,所以我們弄懂這一個(gè)就好了,以下是其他構(gòu)造方法的底層實(shí)現(xiàn)

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

其中默認(rèn)的拒絕策略是

private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

這些構(gòu)造方法中的

        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);

就是那七個(gè)參數(shù)的構(gòu)造方法

有點(diǎn)懵蛹尝?沒(méi)關(guān)系后豫,接下來(lái)我們一個(gè)個(gè)的解析這七個(gè)參數(shù)的意思

三、線程池參數(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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

1. 第一個(gè)參數(shù) corePoolSize 代表這個(gè)線程池的核心線程數(shù)

2. 第二個(gè)參數(shù) maximumPoolSize 代表這個(gè)線程池的最大線程數(shù) (核心線程數(shù) +非核心線程數(shù))

3. 第三個(gè)參數(shù) keepAliveTime 代表這個(gè)線程池的非核心線程的空閑時(shí)的存活時(shí)間

4. 第四個(gè)參數(shù) unit 代表這個(gè)線程池的非核心線程的空閑存活時(shí)間的單位

5. 第五個(gè)參數(shù) workQueue 代表這個(gè)線程池的任務(wù)阻塞隊(duì)列突那,jdk中有幾種常見(jiàn)的阻塞隊(duì)列

  • ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列
  • LinkedBlockingQueue:是一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列
  • SynchronousQueue :同步隊(duì)列挫酿,只存儲(chǔ)一個(gè)任務(wù),插入任務(wù)時(shí)要等待(如果隊(duì)列里有元素的話)取出任務(wù)時(shí)要等待(如果隊(duì)列里沒(méi)有元素的話)
  • PriorityBlockingQueue:優(yōu)先級(jí)隊(duì)列愕难,進(jìn)入隊(duì)列的元素按照優(yōu)先級(jí)會(huì)進(jìn)行排序

建議:建議使用有界隊(duì)列早龟,要是無(wú)界隊(duì)列的話,任務(wù)太多的話可能會(huì)導(dǎo)致OOM

6. 第六個(gè)參數(shù) threadFactory(可以自定義) 代表這個(gè)線程池的創(chuàng)建線程的工廠猫缭,有兩種

  • Executors.privilegedThreadFactory() 使用訪問(wèn)權(quán)限創(chuàng)建一個(gè)權(quán)限控制的線程葱弟。
  • Executors.defaultThreadFactory() 將創(chuàng)建一個(gè)同線程組且默認(rèn)優(yōu)先級(jí)的線程

7. 第七個(gè)參數(shù) handler(可以自定義) 代表這個(gè)線程池的拒絕處理任務(wù)的飽和策略,jdk默認(rèn)提供了四種

  • new ThreadPoolExecutor.AbortPolicy(); 直接拋出異常
  • new ThreadPoolExecutor.CallerRunsPolicy(); 用當(dāng)前調(diào)用者的線程中處理傳過(guò)來(lái)的任務(wù)
  • new ThreadPoolExecutor.DiscardOldestPolicy(); 丟棄最老的一個(gè)任務(wù)猜丹,然后把傳過(guò)來(lái)的任務(wù)加入到阻塞隊(duì)列中
  • new ThreadPoolExecutor.DiscardPolicy(); 什么都不做芝加,直接丟掉傳過(guò)來(lái)的任務(wù)

四、使用線程池例子

基礎(chǔ)概念也都看了射窒,下面來(lái)看個(gè)使用線程池處理任務(wù)的小例子

首先妖混,我們先創(chuàng)建個(gè)任務(wù)類(lèi)

public class Task implements Runnable {
    private String taskName;

    public Task(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        try {
            //模擬每個(gè)任務(wù)的耗時(shí)
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String name = Thread.currentThread().getName();
        System.out.println("這里是xhJaver,線程池系列 當(dāng)前線程名字是 " + name+"  處理了  "+ taskName+"  任務(wù)");
    }
}

我們?cè)賮?lái)看測(cè)試類(lèi)

public class Demo1 {
    public static void main(String[] args) {
        //阻塞隊(duì)列轮洋,設(shè)置阻塞任務(wù)最多為10個(gè)
        BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
        //線程工廠
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話制市,會(huì)由拒絕策略處理剩下的任務(wù)
        ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
        //創(chuàng)建線程池  核心線程數(shù)為5  最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
                        TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy
        );
        for (int i=0;i<10;i++){
            //創(chuàng)建10個(gè)任務(wù),如果要是創(chuàng)建>20個(gè)任務(wù)弊予,則20以外的任務(wù)會(huì)交由拒絕策略處理
            Task task = new Task("task" + i);
            //讓我們自定義的線程池去跑這些任務(wù)
            threadPoolExecutor.execute(task);
        }
         //記得要關(guān)閉線程池
        threadPoolExecutor.shutdown();
    }
}

輸出結(jié)果是

這里是xhJaver祥楣,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task0  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-2  處理了  task1  任務(wù)
這里是xhJaver汉柒,線程池系列 當(dāng)前線程名字是 pool-1-thread-3  處理了  task2  任務(wù)
這里是xhJaver误褪,線程池系列 當(dāng)前線程名字是 pool-1-thread-4  處理了  task3  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-5  處理了  task4  任務(wù)
這里是xhJaver碾褂,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task5  任務(wù)
這里是xhJaver兽间,線程池系列 當(dāng)前線程名字是 pool-1-thread-2  處理了  task6  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-5  處理了  task9  任務(wù)
這里是xhJaver正塌,線程池系列 當(dāng)前線程名字是 pool-1-thread-4  處理了  task8  任務(wù)
這里是xhJaver嘀略,線程池系列 當(dāng)前線程名字是 pool-1-thread-3  處理了  task7  任務(wù)

五恤溶、線程工廠是什么 ?

文中一直說(shuō)線程工廠線程工廠帜羊,這線程工場(chǎng)到底是干嘛的呢咒程? 當(dāng)然是創(chuàng)建線程的工廠啦,創(chuàng)建線程讼育,線程當(dāng)然得有個(gè)名字咯帐姻,就像剛才的小例子輸出的一樣,線程的名字是pool-1-thread-3等等奶段,我現(xiàn)在不想叫這個(gè)名字了饥瓷,那就叫thread-xhJaver吧,這是自定義的名字痹籍,那怎么自定義呢?

首先扛伍,要實(shí)現(xiàn)ThreadFactory接口中的Thread newThread(Runnable r)方法, 傳入一個(gè)任務(wù)词裤,返回一個(gè)自定義線程刺洒,如下面的代碼一樣

public class DIYThreadFactory implements ThreadFactory {

    private AtomicInteger atomicInteger;

    public  DIYThreadFactory( AtomicInteger atomicInteger){
         this.atomicInteger =  atomicInteger;
    }
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setName("xhJaver-thread-"+atomicInteger.getAndIncrement());
        return thread;
    }
}

然后在使用時(shí)傳入這個(gè)自定義的線程工廠

public static void main(String[] args) {
 //阻塞隊(duì)列,設(shè)置阻塞任務(wù)最多為10個(gè)
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
 //創(chuàng)建線程安全的計(jì)數(shù)器
 AtomicInteger atomicInteger = new AtomicInteger();
 //自定義線程工廠
 ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger);
 //拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話吼砂,會(huì)由拒絕策略處理剩下的任務(wù)
ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
 //創(chuàng)建線程池  核心線程數(shù)為5  最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
   TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy
 );
 for (int i=0;i<10;i++){
  //創(chuàng)建10個(gè)任務(wù)逆航,如果要是創(chuàng)建>20個(gè)任務(wù),則20以外的任務(wù)會(huì)交由拒絕策略處理
  Task task = new Task("task" + i);
  //讓我們自定義的線程池去跑這些任務(wù)
  threadPoolExecutor.execute(task);
 }
 //記得要關(guān)閉線程池
 threadPoolExecutor.shutdown();
}

輸出結(jié)果是

這里是xhJaver渔肩,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0  處理了  task0  任務(wù)
這里是xhJaver因俐,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1  處理了  task1  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4  處理了  task4  任務(wù)
這里是xhJaver周偎,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3  處理了  task3  任務(wù)
這里是xhJaver抹剩,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2  處理了  task2  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0  處理了  task5  任務(wù)
這里是xhJaver蓉坎,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1  處理了  task6  任務(wù)
這里是xhJaver澳眷,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2  處理了  task9  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3  處理了  task8  任務(wù)
這里是xhJaver蛉艾,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4  處理了  task7  任務(wù)

我也學(xué)會(huì)了自定義線程工廠了钳踊,可自定義名字到底有用呢,當(dāng)然是排查問(wèn)題拔鸷睢拓瞪!把線程名字定義為和自己業(yè)務(wù)有關(guān)的名字,到時(shí)候報(bào)錯(cuò)的時(shí)候就方便排查了助琐。

六祭埂、 拒絕策略是什么

線程工廠可以自定義,那拒絕策略可以自定義嗎兵钮?當(dāng)然可以啦 方法如下蛆橡,首先也要實(shí)現(xiàn)一個(gè)RejectedExecutionHandler接口舌界,重寫(xiě)rejectedExecution 這個(gè)方法

public class DIYRejectedHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    //記錄日志等操作
        System.out.println("這是xhJaver無(wú)法處理的任務(wù)  "+r.toString()+"  當(dāng)前線程名字是 "+Thread.currentThread().getName());
    }
}

然后在使用時(shí)傳入這個(gè)自定義的拒絕策略

public static void main(String[] args) {
 //阻塞隊(duì)列,設(shè)置阻塞任務(wù)最多為10個(gè)
 BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
 //創(chuàng)建線程安全的計(jì)數(shù)器
 AtomicInteger atomicInteger = new AtomicInteger();
 //自定義線程工廠
 ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger);
 //自定義拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話航罗,會(huì)由拒絕策略處理剩下的任務(wù)
 DIYRejectedHandler diyRejectedHandler = new DIYRejectedHandler();
 //創(chuàng)建線程池  核心線程數(shù)為5  最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
   TimeUnit.SECONDS, blockingQueue, threadFactory, diyRejectedHandler
 );
 for (int i=0;i<30;i++){
  //創(chuàng)建10個(gè)任務(wù),如果要是創(chuàng)建>20個(gè)任務(wù)屁药,則20以外的任務(wù)會(huì)交由拒絕策略處理
  Task task = new Task("task" + i);
  //讓我們自定義的線程池去跑這些任務(wù)
  threadPoolExecutor.execute(task);
 }
 //記得要關(guān)閉線程池
 threadPoolExecutor.shutdown();
}

輸出結(jié)果是

這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task20'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task21'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task22'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task23'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task24'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task25'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task26'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task27'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task28'}  當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task29'}  當(dāng)前線程名字是 main
這里是xhJaver粥血,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5  處理了  task15  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4  處理了  task4  任務(wù)
這里是xhJaver酿箭,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3  處理了  task3  任務(wù)
這里是xhJaver复亏,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2  處理了  task2  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1  處理了  task1  任務(wù)
這里是xhJaver缭嫡,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0  處理了  task0  任務(wù)
這里是xhJaver缔御,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9  處理了  task19  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8  處理了  task18  任務(wù)
這里是xhJaver妇蛀,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7  處理了  task17  任務(wù)
這里是xhJaver耕突,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6  處理了  task16  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4  處理了  task6  任務(wù)
這里是xhJaver评架,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5  處理了  task5  任務(wù)
這里是xhJaver眷茁,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3  處理了  task7  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2  處理了  task8  任務(wù)
這里是xhJaver纵诞,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1  處理了  task9  任務(wù)
這里是xhJaver上祈,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0  處理了  task10  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9  處理了  task11  任務(wù)
這里是xhJaver浙芙,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8  處理了  task12  任務(wù)
這里是xhJaver登刺,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7  處理了  task13  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6  處理了  task14  任務(wù)

七嗡呼、常見(jiàn)的阻塞隊(duì)列及注意點(diǎn)

因?yàn)樽枞?duì)列的知識(shí)太多了纸俭,后續(xù)我們會(huì)單獨(dú)開(kāi)篇來(lái)講這個(gè)阻塞隊(duì)列,先介紹幾個(gè)常用的

1.ArrayBlockingQueue 基于數(shù)組的有界隊(duì)列

2.LinkedBlockingQueue 基于鏈表的無(wú)界隊(duì)列

3.SynchronousQueue

它內(nèi)部只有一個(gè)元素南窗,插入時(shí)如果發(fā)現(xiàn)內(nèi)部有元素未被取走則阻塞掉蔬,取元素時(shí)若隊(duì)列沒(méi)有元素則被阻 塞,直到有元素插入進(jìn)來(lái)矾瘾。

搭配線程池使用如下 女轿,先創(chuàng)建任務(wù)類(lèi)

public class Task implements Runnable {
    private String taskName;

    public Task(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public String toString() {
        return "Task{" +
                "taskName='" + taskName + '\'' +
                '}';
    }

    @Override
    public void run() {
        try {
            //模擬每個(gè)任務(wù)的耗時(shí)
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String name = Thread.currentThread().getName();
        System.out.println("這里是xhJaver,線程池系列 當(dāng)前線程名字是 " + name+"  處理了  "+ taskName+"  任務(wù)");
    }
}

再使用阻塞隊(duì)列

public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i=0;i<10;i++){
            //創(chuàng)建十個(gè)任務(wù)
            Task task = new Task("task" + i);
            //去跑任務(wù)
            executorService.execute(task);
        }
         //記得要關(guān)閉線程池
        executorService.shutdown();
    }

其中newCachedThreadPool底層就使用的是SynchronousQueue

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

輸出結(jié)果是

這里是xhJaver壕翩,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task0  任務(wù)
這里是xhJaver蛉迹,線程池系列 當(dāng)前線程名字是 pool-1-thread-2  處理了  task1  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-5  處理了  task4  任務(wù)
這里是xhJaver放妈,線程池系列 當(dāng)前線程名字是 pool-1-thread-4  處理了  task3  任務(wù)
這里是xhJaver北救,線程池系列 當(dāng)前線程名字是 pool-1-thread-3  處理了  task2  任務(wù)
這里是xhJaver荐操,線程池系列 當(dāng)前線程名字是 pool-1-thread-6  處理了  task5  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-7  處理了  task6  任務(wù)
這里是xhJaver珍策,線程池系列 當(dāng)前線程名字是 pool-1-thread-10  處理了  task9  任務(wù)
這里是xhJaver托启,線程池系列 當(dāng)前線程名字是 pool-1-thread-9  處理了  task8  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-8  處理了  task7  任務(wù)

由此可見(jiàn)攘宙,線程池分別創(chuàng)建了十個(gè)線程來(lái)處理這十個(gè)任務(wù)屯耸,為什么呢? 這是因?yàn)椴渑颐總€(gè)任務(wù)的模擬處理時(shí)間是1s疗绣,當(dāng)再來(lái)的任務(wù)發(fā)現(xiàn)阻塞隊(duì)列中有任務(wù)還沒(méi)被取走,就創(chuàng)建非核心線程處理剛來(lái)的這個(gè)任務(wù)铺韧,不斷的來(lái)任務(wù)多矮,不斷的創(chuàng)建線程,所以用這個(gè)阻塞隊(duì)列再搭配線程池的總線程數(shù)等參數(shù)設(shè)置可能會(huì)因?yàn)椴粩嗟膭?chuàng)建線程而導(dǎo)致OOM哈打。

4.PriorityBlockingQueue 優(yōu)先級(jí)隊(duì)列 進(jìn)入隊(duì)列的元素會(huì)按照任務(wù)的優(yōu)先級(jí)排序塔逃。并且必須實(shí)現(xiàn)Comparable接口。

參數(shù):priorityTask - 要比較的對(duì)象料仗。 返回:負(fù)整數(shù)患雏、零或正整數(shù), 根據(jù)此對(duì)象是小于罢维、等于還是大于指定對(duì)象(要比較的對(duì)象)淹仑。

先創(chuàng)建一個(gè)帶有優(yōu)先級(jí)的任務(wù)

public class PriorityTask implements Runnable , Comparable<PriorityTask>{

    private String taskName;
    // 優(yōu)先級(jí),根據(jù)這個(gè)數(shù)進(jìn)行排序
    private Integer priority;

    public PriorityTask(Integer priority,String taskName) {
        this.priority = priority;
        this.taskName = taskName;
    }

    //這個(gè)compareTo方法的返回值如果是-1的話肺孵,則排序會(huì)認(rèn)為傳過(guò)來(lái)的任務(wù)比此任務(wù)的大珊佣,降序排列
    //這個(gè)compareTo方法的返回值如果是1的話患久,則排序會(huì)認(rèn)為傳過(guò)來(lái)的任務(wù)比此任務(wù)的小,升序排列
    @Override
    public int compareTo(PriorityTask priorityTask) {
        //Integer.compare返回 -1代表 傳過(guò)來(lái)的任務(wù)的priority 比次任務(wù)的priority要小
        // Integer.compare 0   傳過(guò)來(lái)的任務(wù)的priority 比次任務(wù)的priority一樣大
        //Integer.compare  1   傳過(guò)來(lái)的任務(wù)的priority 比次任務(wù)的priority要大
        return Integer.compare(priorityTask.priority,this.priority);
    }

    @Override
    public void run() {
        try {
            //模擬每個(gè)任務(wù)的耗時(shí)
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String name = Thread.currentThread().getName();
        System.out.println("這里是xhJaver,線程池系列 當(dāng)前線程名字是 " + name+"  處理了  "+ taskName+"  任務(wù)");
    }

    @Override
    public String toString() {
        return "Task{" +
                "taskName='" + taskName + '\'' +
                '}';
    }

}

Integer.compare 的 比較大小代碼

java
   public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

測(cè)試代碼


  public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1,
                60L, TimeUnit.SECONDS,
                new PriorityBlockingQueue());
        for (int i=0;i<5;i++){
            //創(chuàng)建十個(gè)任務(wù)
            PriorityTask priorityTask = new PriorityTask(i,"task" + i);
            //去跑任務(wù)
            threadPoolExecutor.execute(priorityTask);
        }
        for (int i=100;i>=95;i--){
            //創(chuàng)建十個(gè)任務(wù)
            PriorityTask priorityTask = new PriorityTask(i,"task" + i);
            //去跑任務(wù)
            threadPoolExecutor.execute(priorityTask);
        }
         //記得要關(guān)閉線程池
        threadPoolExecutor.shutdown();
    }

輸出結(jié)果是

這里是xhJaver名秀,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task0  任務(wù)
這里是xhJaver套腹,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task100  任務(wù)
這里是xhJaver窘行,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task99  任務(wù)
這里是xhJaver僧须,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task98  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task97  任務(wù)
這里是xhJaver紫新,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task96  任務(wù)
這里是xhJaver均蜜,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task95  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task4  任務(wù)
這里是xhJaver芒率,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task3  任務(wù)
這里是xhJaver囤耳,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task2  任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-1  處理了  task1  任務(wù)

由輸出結(jié)果可見(jiàn),除了第一個(gè)以外充择,處理任務(wù)的順序會(huì)按照優(yōu)先級(jí)大小先處理

八德玫、幾種常見(jiàn)的線程池及注意點(diǎn)

他們分別是以下幾種

1.newFixedThreadPool

  • Executors.newFixedThreadPool(10) 它的構(gòu)造方法是
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

有此可見(jiàn),這個(gè)FixedThreadPool線程池的核心線程數(shù)和最大線程數(shù)一樣椎麦,所以就沒(méi)有非核心線程數(shù)宰僧,存活時(shí)間這個(gè)參數(shù)也就是無(wú)效的了,它底層用的是LinkedBlockingQueue這個(gè)阻塞隊(duì)列观挎,這個(gè)隊(duì)列是個(gè)無(wú)界隊(duì)列琴儿,可以點(diǎn)進(jìn)去源碼看它默認(rèn)的容量是Integer.MAX_VALUE

 public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

所以這會(huì)導(dǎo)致一個(gè)什么問(wèn)題呢?就會(huì)導(dǎo)致键兜,當(dāng)核心線程都跑滿的時(shí)候凤类,再來(lái)新任務(wù)的話就會(huì)不斷的添加至這個(gè)阻塞隊(duì)列里面穗泵,一直加一直加普气,但是內(nèi)存是有限的,所以有可能會(huì)出現(xiàn) OOM(OutOfMemory) 的問(wèn)題

  • Executors.newFixedThreadPool(10,Executors.defaultThreadFactory()); 這個(gè)構(gòu)造方法可以傳過(guò)來(lái)指定的創(chuàng)建線程的工廠
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

2.newCachedThreadPool

  • Executors.newCachedThreadPool() 它的構(gòu)造方法是
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

由此可見(jiàn)佃延,它的核心線程數(shù)默認(rèn)是0现诀,線程池總線程容量是Integer.MAX_VALUE,阻塞隊(duì)列用的是SynchronousQueue同步隊(duì)列履肃,非核心線程數(shù)的空閑存活時(shí)間為60s,這會(huì)導(dǎo)致一個(gè)什么問(wèn)題呢仔沿?只要來(lái)了一個(gè)任務(wù),如果沒(méi)有線程的話就創(chuàng)建一個(gè)非核心線程去跑這個(gè)任務(wù)尺棋,如果跑著的過(guò)程中又來(lái)了一個(gè)任務(wù)封锉,就會(huì)繼續(xù)創(chuàng)建線程去跑,以此類(lèi)推膘螟,內(nèi)存是有限的成福,不斷的創(chuàng)建線程的話也會(huì)觸發(fā)OOM問(wèn)題

  • Executors.newCachedThreadPool(Executors.defaultThreadFactory()) 這個(gè)構(gòu)造方法可以傳過(guò)來(lái)指定的創(chuàng)建線程的工廠
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

3.newSingleThreadExecutor

  • Executors.newSingleThreadExecutor() 它的構(gòu)造方法是
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

我們可以看出,它的核心線程數(shù)是一個(gè)荆残,總線程數(shù)也是一個(gè)奴艾。底層用的是LinkedBlockingQueue阻塞隊(duì)列 當(dāng)來(lái)任務(wù)的時(shí)候線程池如果沒(méi)有線程的話,則創(chuàng)建一個(gè)也是唯一一個(gè)線程來(lái)執(zhí)行任務(wù)内斯,剩下的任務(wù)都會(huì)被塞進(jìn)無(wú)界阻塞隊(duì)列里面蕴潦,也是會(huì)有可能產(chǎn)生OOM問(wèn)題。

  • Executors.newSingleThreadExecutor(Executors.defaultThreadFactory()) 這個(gè)構(gòu)造方法可以傳過(guò)來(lái)指定的創(chuàng)建線程的工廠
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

九俘闯、拓展線程池

什么潭苞?線程池還可以拓展?真朗!是的萄传,如果我想記錄下每個(gè)任務(wù)的執(zhí)行開(kāi)始情況,結(jié)束情況,線程池關(guān)閉情況就要拓展啦秀菱,ThreadPoolExecutor它內(nèi)部是提供了幾個(gè)方法給我們拓展振诬,其中beforeExecute、afterExecute衍菱、terminated赶么,這三個(gè)分別對(duì)應(yīng)任務(wù)開(kāi)始,任務(wù)結(jié)束脊串,線程池關(guān)閉的三種情況辫呻,所以我們就要重寫(xiě)他們啦,話不多說(shuō)琼锋,看下代碼

public static void main(String[] args) {
 //阻塞隊(duì)列放闺,設(shè)置阻塞任務(wù)最多為10個(gè)
 BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
 //創(chuàng)建線程安全的計(jì)數(shù)器
 AtomicInteger atomicInteger = new AtomicInteger();
 //自定義線程工廠
 ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger);
 //自定義拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話,會(huì)由拒絕策略處理剩下的任務(wù)
 DIYRejectedHandler diyRejectedHandler = new DIYRejectedHandler();
 //創(chuàng)建線程池  核心線程數(shù)為5  最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
   TimeUnit.SECONDS, blockingQueue, threadFactory, diyRejectedHandler
 ){
  @Override
  protected void beforeExecute(Thread t, Runnable r) {
   System.out.println("xhJaver 當(dāng)前線程是"+t.getName()+"開(kāi)始處理任務(wù):"+r.toString());
  }

  @Override
  protected void afterExecute(Runnable r, Throwable t) {
   if(t!=null){
    System.out.println("xhJaver 當(dāng)前線程是"+Thread.currentThread().getName() +"處理任務(wù)結(jié)束:"+r.toString()+" 錯(cuò)誤是 "+ t);
   }
   System.out.println("xhJaver 當(dāng)前線程是"+Thread.currentThread().getName() +"處理任務(wù)結(jié)束:"+r.toString()+" 沒(méi)有錯(cuò)誤 ");

  }

  @Override
  protected void terminated() {
   System.out.println("xhJaver 當(dāng)前線程是"+Thread.currentThread().getName() +"關(guān)閉線程池");
  }
 };
 for (int i=0;i<21;i++){
  //創(chuàng)建10個(gè)任務(wù)缕坎,如果要是創(chuàng)建>20個(gè)任務(wù)怖侦,則20以外的任務(wù)會(huì)交由拒絕策略處理
  Task task = new Task("task" + i);
  //讓我們自定義的線程池去跑這些任務(wù)
  threadPoolExecutor.execute(task);
 }
 //記得要關(guān)閉線程池
 threadPoolExecutor.shutdown();
}

輸出結(jié)果是

這是xhJaver無(wú)法處理的任務(wù)  Task{taskName='task20'}  當(dāng)前線程名字是 main
xhJaver 當(dāng)前線程是xhJaver-thread-7開(kāi)始處理任務(wù):Task{taskName='task17'}
xhJaver 當(dāng)前線程是xhJaver-thread-6開(kāi)始處理任務(wù):Task{taskName='task16'}
xhJaver 當(dāng)前線程是xhJaver-thread-9開(kāi)始處理任務(wù):Task{taskName='task19'}
xhJaver 當(dāng)前線程是xhJaver-thread-4開(kāi)始處理任務(wù):Task{taskName='task4'}
xhJaver 當(dāng)前線程是xhJaver-thread-8開(kāi)始處理任務(wù):Task{taskName='task18'}
xhJaver 當(dāng)前線程是xhJaver-thread-2開(kāi)始處理任務(wù):Task{taskName='task2'}
xhJaver 當(dāng)前線程是xhJaver-thread-3開(kāi)始處理任務(wù):Task{taskName='task3'}
xhJaver 當(dāng)前線程是xhJaver-thread-5開(kāi)始處理任務(wù):Task{taskName='task15'}
xhJaver 當(dāng)前線程是xhJaver-thread-0開(kāi)始處理任務(wù):Task{taskName='task0'}
xhJaver 當(dāng)前線程是xhJaver-thread-1開(kāi)始處理任務(wù):Task{taskName='task1'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4  處理了  task4  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-4處理任務(wù)結(jié)束:Task{taskName='task4'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-4開(kāi)始處理任務(wù):Task{taskName='task5'}
這里是xhJaver谜叹,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9  處理了  task19  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-9處理任務(wù)結(jié)束:Task{taskName='task19'} 沒(méi)有錯(cuò)誤 
這里是xhJaver匾寝,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6  處理了  task16  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-6處理任務(wù)結(jié)束:Task{taskName='task16'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-9開(kāi)始處理任務(wù):Task{taskName='task6'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7  處理了  task17  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-7處理任務(wù)結(jié)束:Task{taskName='task17'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-7開(kāi)始處理任務(wù):Task{taskName='task8'}
xhJaver 當(dāng)前線程是xhJaver-thread-6開(kāi)始處理任務(wù):Task{taskName='task7'}
這里是xhJaver荷腊,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1  處理了  task1  任務(wù)
這里是xhJaver艳悔,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8  處理了  task18  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-8處理任務(wù)結(jié)束:Task{taskName='task18'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-8開(kāi)始處理任務(wù):Task{taskName='task9'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2  處理了  task2  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-2處理任務(wù)結(jié)束:Task{taskName='task2'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-2開(kāi)始處理任務(wù):Task{taskName='task10'}
這里是xhJaver女仰,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3  處理了  task3  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-3處理任務(wù)結(jié)束:Task{taskName='task3'} 沒(méi)有錯(cuò)誤 
這里是xhJaver猜年,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5  處理了  task15  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-5處理任務(wù)結(jié)束:Task{taskName='task15'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-5開(kāi)始處理任務(wù):Task{taskName='task12'}
xhJaver 當(dāng)前線程是xhJaver-thread-1處理任務(wù)結(jié)束:Task{taskName='task1'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-1開(kāi)始處理任務(wù):Task{taskName='task13'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0  處理了  task0  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-3開(kāi)始處理任務(wù):Task{taskName='task11'}
xhJaver 當(dāng)前線程是xhJaver-thread-0處理任務(wù)結(jié)束:Task{taskName='task0'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-0開(kāi)始處理任務(wù):Task{taskName='task14'}
這里是xhJaver疾忍,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4  處理了  task5  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-4處理任務(wù)結(jié)束:Task{taskName='task5'} 沒(méi)有錯(cuò)誤 
這里是xhJaver乔外,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6  處理了  task7  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-6處理任務(wù)結(jié)束:Task{taskName='task7'} 沒(méi)有錯(cuò)誤 
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9  處理了  task6  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-9處理任務(wù)結(jié)束:Task{taskName='task6'} 沒(méi)有錯(cuò)誤 
這里是xhJaver锭碳,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7  處理了  task8  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-7處理任務(wù)結(jié)束:Task{taskName='task8'} 沒(méi)有錯(cuò)誤 
這里是xhJaver袁稽,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2  處理了  task10  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-2處理任務(wù)結(jié)束:Task{taskName='task10'} 沒(méi)有錯(cuò)誤 
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8  處理了  task9  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-8處理任務(wù)結(jié)束:Task{taskName='task9'} 沒(méi)有錯(cuò)誤 
這里是xhJaver擒抛,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5  處理了  task12  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-5處理任務(wù)結(jié)束:Task{taskName='task12'} 沒(méi)有錯(cuò)誤 
這里是xhJaver推汽,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0  處理了  task14  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-0處理任務(wù)結(jié)束:Task{taskName='task14'} 沒(méi)有錯(cuò)誤 
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3  處理了  task11  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-3處理任務(wù)結(jié)束:Task{taskName='task11'} 沒(méi)有錯(cuò)誤 
這里是xhJaver歧沪,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1  處理了  task13  任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-1處理任務(wù)結(jié)束:Task{taskName='task13'} 沒(méi)有錯(cuò)誤 
xhJaver 當(dāng)前線程是xhJaver-thread-1關(guān)閉線程池

更多精彩請(qǐng)關(guān)注公眾號(hào)xhJaver,京東java工程師和你一起成長(zhǎng)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歹撒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诊胞,更是在濱河造成了極大的恐慌暖夭,老刑警劉巖锹杈,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異迈着,居然都是意外死亡竭望,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)裕菠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)咬清,“玉大人,你說(shuō)我怎么就攤上這事奴潘【缮眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵画髓,是天一觀的道長(zhǎng)掘剪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)奈虾,這世上最難降的妖魔是什么夺谁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮愚墓,結(jié)果婚禮上予权,老公的妹妹穿的比我還像新娘昂勉。我一直安慰自己浪册,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布岗照。 她就那樣靜靜地躺著村象,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攒至。 梳的紋絲不亂的頭發(fā)上厚者,一...
    開(kāi)封第一講書(shū)人閱讀 52,166評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音迫吐,去河邊找鬼库菲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛志膀,可吹牛的內(nèi)容都是我干的熙宇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼溉浙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼烫止!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起戳稽,我...
    開(kāi)封第一講書(shū)人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馆蠕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體互躬,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡播赁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吼渡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片行拢。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖诞吱,靈堂內(nèi)的尸體忽然破棺而出舟奠,到底是詐尸還是另有隱情,我是刑警寧澤房维,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布沼瘫,位于F島的核電站,受9級(jí)特大地震影響咙俩,放射性物質(zhì)發(fā)生泄漏耿戚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一阿趁、第九天 我趴在偏房一處隱蔽的房頂上張望膜蛔。 院中可真熱鬧,春花似錦脖阵、人聲如沸皂股。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)呜呐。三九已至,卻和暖如春悍募,著一層夾襖步出監(jiān)牢的瞬間蘑辑,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工坠宴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洋魂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓喜鼓,卻偏偏與公主長(zhǎng)得像副砍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颠通,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

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