4、線程池

一、前期知識(shí)概要

1、設(shè)計(jì)模式對(duì)象池(資源池)

在我們的日常生活我們聽過(guò)水池,電池等等,水池了用來(lái)存放水,電池用來(lái)存放電,而在編程的世界中的池是用來(lái)存放一組資源

資源池(Resource pool)也叫對(duì)象池(Object pool) 被認(rèn)為是一種設(shè)計(jì)模式,這里的資源主要是指系統(tǒng)資源, 這些資源不專屬于某個(gè)進(jìn)程或內(nèi)部資源。客戶端向池請(qǐng)求資源, 并使用返回的資源進(jìn)行指定的操作。當(dāng)客戶端使用完資源后, 會(huì)把資源放回池中而不是釋放或丟棄掉。

總結(jié)一句話: 需要時(shí),從池中提取,不用時(shí),放回池中

舉個(gè)栗子: 對(duì)象池就想我們公司的倉(cāng)庫(kù),比如我們?nèi)ス旧习?公司會(huì)給我們提供一個(gè)工位,行政人員會(huì)給我們提供相應(yīng)的辦公設(shè)備,那這個(gè)時(shí)候她首先會(huì)看一下庫(kù)房中,如果庫(kù)房中有,直接從庫(kù)房中拿,如果庫(kù)房中沒有,那就會(huì)去網(wǎng)上或者商店購(gòu)買一個(gè)新的。如果員工離職了正常情況下會(huì)將員工的能用的辦公物品放到庫(kù)房。

image

設(shè)計(jì)模式

2、應(yīng)用場(chǎng)景

它用在當(dāng)對(duì)象的初始化過(guò)程代價(jià)較大或者使用頻率較高時(shí),比如線程池,數(shù)據(jù)庫(kù)連接池等。運(yùn)用對(duì)象池化技術(shù)可以顯著地提升性能。

二、為什么要使用

創(chuàng)建線程對(duì)象不像其他對(duì)象一樣在JVM分配內(nèi)存即可,還要調(diào)用操作系統(tǒng)內(nèi)核的API,然后操作系統(tǒng)為線程分配一系列的資源,這個(gè)成本就很高了。所以線程是一個(gè)重量級(jí)對(duì)象,應(yīng)該避免頻繁創(chuàng)建和銷毀

降低資源消耗。 通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。

提高響應(yīng)速度。 當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。

提高線程的可管理性。 線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。

引用《Java并發(fā)編程的藝術(shù)》

三、Java線程池的架構(gòu)設(shè)計(jì)

1、說(shuō)明

Java里面線程池的頂級(jí)接口是Executor,該類位于java.util.concurrent,但是嚴(yán)格意義上講Executor并不是一個(gè)線程池,而只是一個(gè)執(zhí)行線程的工具。真正的線程池接口是ExecutorService。

2、重要類說(shuō)明

類或者接口 說(shuō)明
ExecutorService 真正的線程池接口。
ScheduledExecutorService 定時(shí)任務(wù)與線程池功能結(jié)合使用
ThreadPoolExecutor ExecutorService的默認(rèn)實(shí)現(xiàn)。重點(diǎn)
ScheduledThreadPoolExecutor 周期性任務(wù)調(diào)度。

3、結(jié)構(gòu)圖

image

image

4、Executor

  1. 說(shuō)明
    Executor接口只有一個(gè)execute方法,執(zhí)行提交Runnable任務(wù),用來(lái)替代通常啟動(dòng)線程的方法
  2. 方法
    execute(Runnable r)
    
  3. 舉個(gè)栗子
    /*以前*/
    Thread t = new Thread();
    t.start();
    /*使用線程池*/
    Thread t = new Thread();
    executor.execute(t)
    

5、ExecutorService

  1. 說(shuō)明
    ExecutorService接口繼承自Executor接口,真正的線程池核心類。提供了管理終止的方法,以及可為跟蹤一個(gè)或多個(gè)異步任務(wù)執(zhí)行狀況而生成 Future 的方法。增加了shutDown(),shutDownNow(),invokeAll(),invokeAny()和submit()等方法。如果需要支持即時(shí)關(guān)閉,也就是shutDownNow()方法,則任務(wù)需要正確處理中斷。
  2. 核心方法
    方法名 返回值 說(shuō)明
    **submit(Callable task) ** Future<T> 提交一個(gè)可運(yùn)行的任務(wù)執(zhí)行,并返回一個(gè)表示該任務(wù)結(jié)果
    submit(Runable task) Future<T> 提交一個(gè)可運(yùn)行的任務(wù)執(zhí)行,并返回一個(gè)表示該任務(wù)結(jié)果
    shutdown() 布爾 阻止新來(lái)的任務(wù)提交,對(duì)已經(jīng)提交了的任務(wù)不會(huì)產(chǎn)生任何影響。當(dāng)已經(jīng)提交的任務(wù)執(zhí)行完后,它會(huì)將那些閑置的線程進(jìn)行中斷,這個(gè)過(guò)程是異步的
    shutdownNow() List<Runable> 設(shè)置線程池的狀態(tài)為STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表
    isShutdown() 布爾 檢測(cè)線程池是否正處于關(guān)閉中
    isTerminated() 布爾 所有任務(wù)在關(guān)閉后完成,則返回 true 。
    awaitTermination() 布爾 定時(shí)或者永久等待線程池關(guān)閉結(jié)束
  3. 舉個(gè)栗子
        private static int TASK_COUNT = 10;
        public static void main(String[] args) {
            /*1. 創(chuàng)建線程池對(duì)象 */
            ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 8, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));
            /*2. 提交任務(wù)*/
            for (int i = 0; i < TASK_COUNT; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + ":----->在執(zhí)行任務(wù)");
                });
            }
            /*3. 關(guān)閉連接池*/
            pool.shutdown();
           /* 或者 */
         // pool.shutdownNow()
        }
    

6、ScheduledExecutorService

  1. 說(shuō)明
    ScheduledExecutorService是基于線程池設(shè)計(jì)的定時(shí)任務(wù)類,每個(gè)調(diào)度任務(wù)都會(huì)分配到線程池中的一個(gè)線程去執(zhí)行,任務(wù)是并發(fā)執(zhí)行,互不影響。
  2. 關(guān)系圖


    image
  3. 示例代碼
        public static void main(String[] args) {
            // 創(chuàng)建定時(shí)任務(wù)線程池
            ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
            //設(shè)置日期格式
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
           // 提交一個(gè)任務(wù)兩秒之后開始執(zhí)行
            executorService.schedule(() -> {
                System.out.println("++++++++++++++++++++子線程:" + df.format(new Date()));
            }, 2, TimeUnit.SECONDS);
            System.out.println("主線程:    " + df.format(new Date()));
    //        executorService.shutdown();
        }
    

7、工作流程(了解)

image

四、線程池的狀態(tài)與生命周期

1、概要

線程池有運(yùn)行、關(guān)閉、停止、清空狀態(tài)、結(jié)束五種狀態(tài),結(jié)束后就會(huì)釋放所有資源

  1. RUNNING(運(yùn)行): 接受新的任務(wù)和處理隊(duì)列中的任務(wù)
  2. SHUTDOWN(關(guān)閉): 不接受新的請(qǐng)求,但會(huì)處理已經(jīng)添加到隊(duì)列中的任務(wù)
  3. STOP(停止): 不接收新任務(wù),也不處理隊(duì)列任務(wù),并且中斷所有處理中的任務(wù)。
  4. TIDYING(整理):所有任務(wù)都被終結(jié),有效線程為0。會(huì)觸發(fā)terminated()方法
  5. TERMINATED(結(jié)束):當(dāng)terminated()方法執(zhí)行結(jié)束

2、流程圖

image

五、ThreadPoolExecutor

1、線程池的創(chuàng)建

  1. 構(gòu)造方法
    ThreadPoolExecutor(int corePoolSize,
                       int maximumPoolSize,
                       long keepAliveTime,
                       TimeUnit unit,
                       BlockingQueue<Runnable> workQueue,
                       ThreadFactory threadFactory,
                       RejectedExecutionHandler handler)
    

2、參數(shù)概要

參數(shù) 概要
corePoolSize 池中所保存的線程數(shù),包括空閑線程。
maximumPoolSize 池中允許的最大線程數(shù)。
keepAliveTime 當(dāng)線程數(shù)大于核心時(shí),此為終止前多余的空閑線程等待新任務(wù)的最長(zhǎng)時(shí)間。
unit - keepAliveTime 時(shí)間單位。
workQueue 執(zhí)行前用于保持任務(wù)的隊(duì)列。此隊(duì)列僅保持由 execute方法提交的 Runnable任務(wù)。
threadFactory 執(zhí)行程序創(chuàng)建新線程時(shí)使用的工廠
handler 由于超出線程范圍和隊(duì)列容量而使執(zhí)行被阻塞時(shí)所使用的處理程序。

3、參數(shù)詳解

3.1、corePoolSize(必要參數(shù))

核心線程數(shù)。默認(rèn)情況下,核心線程會(huì)一直存活,但是當(dāng)將allowCoreThreadTimeout設(shè)置為true時(shí),核心線程也會(huì)超時(shí)回收。

3.2、maximumPoolSize(必要參數(shù))

線程池允許創(chuàng)建的最大線程數(shù)。如果隊(duì)列滿了,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會(huì)再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是如果使用了無(wú)界的任務(wù)隊(duì)列這個(gè)參數(shù)就沒什么效果。

3.3、keepAliveTime(必要參數(shù))

線程池的工作線程空閑后,保持存活的時(shí)間。所以如果任務(wù)很多,并且每個(gè)任務(wù)執(zhí)行的時(shí)間比較短,可以調(diào)大這個(gè)時(shí)間,提高線程的利用率。

3.4、unit(必要參數(shù))

指定keepAliveTime參數(shù)的時(shí)間單位

可選值 說(shuō)明
TimeUnit.DAYS
TimeUnit.HOURS 小時(shí)
TimeUnit.MINUTES(常用) 分鐘
TimeUnit.SECONDS(常用)
TimeUnit.MILLISECONDS(常用) 毫秒
TimeUnit.MICROSECONDS 微秒(千分之一毫秒)
TimeUnit.NANOSECONDS 毫微秒(千分之一微秒)

3.5、workQueue

任務(wù)隊(duì)列。Runnable對(duì)象就存儲(chǔ)在該參數(shù)中

3.6、threadFactory(可選)

線程工廠。用于指定為線程池創(chuàng)建新線程的方式

3.7、handler(可選)

  1. 說(shuō)明
    拒絕策略。有兩種情況會(huì)觸發(fā)拒絕策略
    • 隊(duì)列和線程池都滿了,說(shuō)明線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)。這個(gè)策略默認(rèn)情況下是 AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常
    • 當(dāng)線程池被調(diào)用shutdown()后
  2. 可選值
    策略 說(shuō)明
    AbortPolicy 直接拋出異常。默認(rèn)值
    CallerRunsPolicy 只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)。
    DiscardOldestPolicy 丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。
    DiscardPolicy 不處理,丟棄掉。

4、舉個(gè)栗子

  1. 有返回值
     public static void start() {
            /*
             * 創(chuàng)建線程池,并發(fā)量最大為5
             * LinkedBlockingDeque,表示執(zhí)行任務(wù)或者放入隊(duì)列
             */
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0,
                    TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            // 存儲(chǔ)線程的返回值
            List<Future<String>> results = new LinkedList<>();
            for (int i = 0; i < 10; i++) {
                // 調(diào)用submit可以獲得線程的返回值
                int num = i;
                Future<String> result = executor.submit(() -> num + "");
                results.add(result);
            }
            //如果不調(diào)用,awaitTermination將一直阻塞
            executor.shutdown();
            //1天,模擬永遠(yuǎn)等待
            try {
                System.out.println(executor.awaitTermination(1, TimeUnit.DAYS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //輸出結(jié)果
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(results.get(i).get());
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    

5、corePoolSize、workQueue 、maximumPoolSize的關(guān)系

  1. 默認(rèn)情況下,線程池在初始的時(shí)候,線程數(shù)為0。當(dāng)接收到一個(gè)任務(wù)時(shí),如果線程池中存活的線程數(shù)小于corePoolSize核心線程,則新建一個(gè)線程。
  2. 如果所有運(yùn)行的核心線程都都在忙,超出核心線程處理的任務(wù),執(zhí)行器更多地選擇把任務(wù)放進(jìn)隊(duì)列,而不是新建一個(gè)線程。
  3. 如果一個(gè)任務(wù)提交不了到隊(duì)列,在不超出最大線程數(shù)量情況下,會(huì)新建線程。就根據(jù)指定的拒絕策略來(lái)處理,默認(rèn)拋出異常。
  4. 如線程閑置時(shí),線程池會(huì)根據(jù)keepAliveTime設(shè)置的時(shí)間回收大于corePoolSize的線程

六、ScheduledThreadPoolExecutor

1、簡(jiǎn)介

ScheduledThreadPoolExecutor用來(lái)執(zhí)行周期性任務(wù)的調(diào)度。在這之前的實(shí)現(xiàn)需要依靠Timer和TimerTask或者其它第三方工具來(lái)完成。它主要有以下兩個(gè)作用

  1. 延時(shí)執(zhí)行任務(wù)。
  2. 周期性重復(fù)執(zhí)行任務(wù)。
Timer ScheduledThreadPoolExecutor
單線程 多線程
單個(gè)任務(wù)執(zhí)行時(shí)間影響其他任務(wù)調(diào)度 多線程,不會(huì)影響
基于絕對(duì)時(shí)間 基于相對(duì)時(shí)間
一旦執(zhí)行任務(wù)出現(xiàn)異常不會(huì)捕獲,其他任務(wù)得不到執(zhí)行 多線程,單個(gè)任務(wù)的執(zhí)行不會(huì)影響其他線程

2、示例代碼

  1. 執(zhí)行一次
    private static final int TASK_COUNT = 3;
    public static void main(String[] args) throws InterruptedException {
        // 創(chuàng)建大小為2的線程池
        ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(2);
        for (int i = 0; i < TASK_COUNT; i++) {
            // 只執(zhí)行一次
          scheduledThreadPool.schedule(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, 5, TimeUnit.SECONDS);
        }
        // 關(guān)閉線程池
        scheduledThreadPool.shutdown();
        boolean isStop;
        // 等待線程池終止
        do {
            isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.HOURS);
            System.out.println("等待任務(wù)結(jié)束中...");
        } while (!isStop);
        System.out.println("所有工作完成!!! 線程池關(guān)閉");
    }
  1. 周期執(zhí)行任務(wù)
        private static final int TASK_COUNT = 3;
        public static void main(String[] args) throws InterruptedException {
            // 1. 創(chuàng)建大小為2的線程池
            ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(2);
                // 2. 周期性執(zhí)行,每2秒執(zhí)行一次
                scheduledThreadPool.scheduleAtFixedRate(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }, 0, 2, TimeUnit.SECONDS);
            }
            // 3.關(guān)閉線程池
            scheduledThreadPool.shutdown();
            boolean isStop;
            // 等待線程池終止
            do {
                isStop = scheduledThreadPool.awaitTermination(1, TimeUnit.HOURS);
                System.out.println("等待任務(wù)結(jié)束中...");
            } while (!isStop);
             System.out.println("所有工作完成!!! 線程池關(guān)閉");
        }
    

七、任務(wù)隊(duì)列

1、名詞解釋

1.1、什么叫有界

有界就是有固定大小的隊(duì)列,無(wú)界表示無(wú)上限

1.2、什么叫隊(duì)列

Queue 一個(gè)隊(duì)列就是一個(gè)先入先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)

Queue接口與List、Set同一級(jí)別,都是繼承了Collection接口。

2、常用隊(duì)列

2.1、ArrayBlockingQueue

  1. 作用
    采用數(shù)組實(shí)現(xiàn)的有界阻塞線程安全隊(duì)列。如果向已滿的隊(duì)列繼續(xù)塞入元素,將導(dǎo)致當(dāng)前的線程阻塞。如果向空隊(duì)列獲取元素,那么將導(dǎo)致當(dāng)前線程阻塞。
  2. 構(gòu)造方法
    構(gòu)造方法 參數(shù)說(shuō)明
    public ArrayBlockingQueue(int capacity) 構(gòu)造指定大小的有界隊(duì)列
    public ArrayBlockingQueue(int capacity, boolean fair) 構(gòu)造指定大小的有界隊(duì)列,指定為公平或非公平鎖
    public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) 構(gòu)造指定大小的有界隊(duì)列,指定為公平或非公平鎖,指定在初始化時(shí)加入一個(gè)集合
  3. 示例代碼
    public class ArrayBlockingQueueExample {
        public static final int COUNT = 100;
        public static void main(String[] args) throws Exception {
            ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 8, 60, TimeUnit.SECONDS, queue);
            for (int i = 0; i < COUNT; i++) {
                TimeUnit.SECONDS.sleep(1);
                executor.execute(() ->
                        System.out.println("線程池---數(shù)組實(shí)現(xiàn)的有界阻塞線程安全隊(duì)列" + Thread.currentThread().getName()));
            }
            executor.shutdown();
        }
    }
    

2.2、LinkedBlockingQueue

  1. 作用
    一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列(也可以當(dāng)無(wú)界阻塞隊(duì)列)。此隊(duì)列按 FIFO(先進(jìn)先出)原則。Executor.newFixedThreadPool()默認(rèn)隊(duì)列
  2. 構(gòu)造方法
    構(gòu)造方法 參數(shù)說(shuō)明
    public LinkedBlockingQueue() 在未指明容量時(shí),容量默認(rèn)為Integer.MAX_VALUE
    public LinkedBlockingQueue(int capacity) 構(gòu)造指定大小的有界隊(duì)列

2.3、SynchronousQueue;

  1. 作用
    一個(gè)不存儲(chǔ)元素的有界阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool()使用了這個(gè)隊(duì)列。
  2. 構(gòu)造方法
    構(gòu)造方法 參數(shù)說(shuō)明
    public SynchronousQueue() 默認(rèn)情況下不保證排序,
    public SynchronousQueue(boolean fair) 如果設(shè)置true隊(duì)列可保證線程以 FIFO 的順序進(jìn)行訪問(wèn)
  3. 示例代碼
    public class SynchronousQueueExample {
        public static final int COUNT = 100;
        public static void main(String[] args) {
            SynchronousQueue<Runnable> queue = new SynchronousQueue<>();
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, Integer.MAX_VALUE, 1, TimeUnit.SECONDS, queue);
            for (int i = 0; i < COUNT; i++) {
                executor.execute(() ->
                        System.out.println("線程池---同步隊(duì)列" + Thread.currentThread().getName()));
            }
            executor.shutdown();
        }
    }
    
  4. 分析
    • 假設(shè)當(dāng)前有2個(gè)核心線程
    • 此時(shí)來(lái)了一個(gè)任務(wù)(A),根據(jù)前面介紹的“如果運(yùn)行的線程等于或多于 corePoolSize,則 Executor 始終首選將請(qǐng)求加入隊(duì)列,而不添加新的線程?!?所以A被添加到queue中。
    • 又來(lái)了一個(gè)任務(wù)(B),且核心2個(gè)線程還沒有忙完。接下來(lái)首先嘗試1中描述,但是由于使用的SynchronousQueue,所以一定無(wú)法加入進(jìn)去。
    • 此時(shí)便滿足了上面提到的“如果無(wú)法將請(qǐng)求加入隊(duì)列,則創(chuàng)建新的線程”,所以必然會(huì)新建一個(gè)線程來(lái)運(yùn)行這個(gè)任務(wù)。
    • 但是如果這三個(gè)任務(wù)都還沒完成,繼續(xù)來(lái)了一個(gè)任務(wù),queue中無(wú)法插入(任務(wù)A還在queue中),而線程數(shù)達(dá)到了maximumPoolSize,所以只好執(zhí)行異常策略了。
      為了避免這種情況:,所以在使用SynchronousQueue通常要求maximumPoolSize是無(wú)界的(如果希望限制就直接使用有界隊(duì)列)。對(duì)于使用SynchronousQueue的作用jdk中寫的很清楚:此策略可以避免在處理可能具有內(nèi)部依賴性的請(qǐng)求集時(shí)出現(xiàn)鎖。

2.4、PriorityBlockingQueue

  1. 作用
    一種優(yōu)先級(jí)隊(duì)列,元素并不是以FIFO的方式出/入隊(duì)。默認(rèn)大小為11,不可以插入 null 值。當(dāng)隊(duì)列滿的時(shí)候會(huì)進(jìn)行擴(kuò)容,是真正意義上的無(wú)界(僅受內(nèi)存大小限制),它不像ArrayBlockingQueue那樣構(gòu)造時(shí)必須指定最大容量,也不像LinkedBlockingQueue默認(rèn)最大容量為Integer.MAX_VALUE;
  2. 構(gòu)造方法
    構(gòu)造方法 參數(shù)說(shuō)明
    PriorityBlockingQueue()
    PriorityBlockingQueue(int initialCapacity) 指定初始化隊(duì)列長(zhǎng)度
    PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) 指定初始化隊(duì)列長(zhǎng)度,自定義比較器

八、Executors(了解)

1、說(shuō)明

對(duì)于新手來(lái)說(shuō)要配置一個(gè)線程池還是比較有難度的,尤其是對(duì)于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優(yōu)的,因此在Executors類里面提供了一些靜態(tài)工廠,生成一些常用的線程池

2、注意注意注意

image

3、Executors靜態(tài)方法

1、newSingleThreadExecutor

  1. 作用
    創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它。
  2. 方法
    Executors.newSingleThreadExecutor()
    
  3. 應(yīng)用場(chǎng)景
    保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行
    不適合并發(fā)但可能引起IO阻塞性及影響UI線程響應(yīng)的操作,如數(shù)據(jù)庫(kù)操作、文件操作等

2、newFixedThreadExecutor

  1. 作用
    創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程
  2. 方法
    Executors.newFixedThreadExecutor()
    
  3. 應(yīng)用場(chǎng)景
    控制線程最大并發(fā)數(shù)
  4. 舉個(gè)栗子
        public static void main(String[] args) throws IOException, InterruptedException {
            // 創(chuàng)建一個(gè)固定大小的線程池
            ExecutorService service = Executors.newFixedThreadPool(3);
            for (int i = 0; i < 10; i++) {
                System.out.println("創(chuàng)建線程" + i);
                Runnable run = new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("啟動(dòng)線程");
                    }
                };
                // 在未來(lái)某個(gè)時(shí)間執(zhí)行給定的命令
                service.execute(run);
            }
            // 關(guān)閉啟動(dòng)線程
            service.shutdown();
            // 等待子線程結(jié)束,再繼續(xù)執(zhí)行下面的代碼
            service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
            System.out.println("等待所有線程執(zhí)行完成");
        }
    }
    
  5. 注意
    newFixedThreadPool線程池的線程是不會(huì)釋放的,即使它是閑置的。這就會(huì)產(chǎn)生性能問(wèn)題

3、newCacheThreadExecutor

  1. 作用
    創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程,
    那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小
  2. 方法
    Executors.newCacheThreadExecutor()
    
  3. 應(yīng)用場(chǎng)景
    適合執(zhí)行大量、耗時(shí)少的任務(wù)
  4. 舉個(gè)栗子
    public class ThreadPoolCached {
     public static void main(String[] args) {
       ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
       for (int i = 0; i < 100; i++) {
         final int index = i;
         try {
           Thread.sleep(index * 100);
         } catch (Exception e) {
           e.printStackTrace();
         }
         cachedThreadPool.execute(new Runnable() {
           @Override
           public void run() {
             System.out.println("當(dāng)前線程"+Thread.currentThread().getName());
           }
         });
       }
     }
    

4、newScheduleThreadExecutor

  1. 作用
    創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求
  2. 方法
    Executors.newScheduleThreadExecutor()
    
  3. 示例代碼
    // 1. 創(chuàng)建 定時(shí)線程池對(duì)象 & 設(shè)置線程池線程數(shù)量固定為5
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    // 2. 創(chuàng)建好Runnable類線程對(duì)象 & 需執(zhí)行的任務(wù)
    Runnable task =new Runnable(){
     public void run() {
     System.out.println("執(zhí)行任務(wù)啦");
     }
    };
    // 3. 向線程池提交任務(wù)
    scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延遲1s后執(zhí)行任務(wù)
    scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms后、每隔1000ms執(zhí)行任務(wù)
    

九、面試題

1、shutdown

  1. shutdown()有什么作用?
    阻止新來(lái)的任務(wù)提交,對(duì)已經(jīng)提交的任務(wù)不會(huì)產(chǎn)生任何影響 當(dāng)已經(jīng)提交的任務(wù)執(zhí)行完成之后,那些閑置的線程會(huì)被回收
    這個(gè)過(guò)程是異步的。
  2. 如何阻止新來(lái)的任務(wù)提交?
    通過(guò)將線程池的狀態(tài)改成SHUTDOWN,當(dāng)再將執(zhí)行execute提交任務(wù)時(shí),如果測(cè)試到狀態(tài)不為RUNNING,則拋出rejectedExecution,從而達(dá)到阻止新任務(wù)提交的目的。
  3. 為何對(duì)提交的任務(wù)不產(chǎn)生任何影響?
    在調(diào)用中斷任務(wù)的方法時(shí),它會(huì)檢測(cè)workers中的任務(wù),如果worker對(duì)應(yīng)的任務(wù)沒有中斷,并且是空閑線程,它才會(huì)去中斷。另外的話,workQueue中的值,還是按照一定的邏輯順序不斷的往works中進(jìn)行輸送的,這樣一來(lái),就可以保證提交的任務(wù)按照線程本身的邏輯執(zhí)行,不受到影響。

2、shutdownNow

  1. shutdownNow()有什么功能?
    阻止新來(lái)的任務(wù)提交,同時(shí)會(huì)中斷當(dāng)前正在運(yùn)行的線程,即workers中的線程。另外它還將workQueue中的任務(wù)給移除,并將這些任務(wù)添加到列表中進(jìn)行返回。
  2. 如何阻止新來(lái)的任務(wù)提交?
    通過(guò)將線程池的狀態(tài)改成STOP,當(dāng)再將執(zhí)行execute提交任務(wù)時(shí),如果測(cè)試到狀態(tài)不為RUNNING,則拋出rejectedExecution,從而達(dá)到阻止新任務(wù)提交的目的.
  3. 如果我提交的任務(wù)代碼塊中,正在等待某個(gè)資源,而這個(gè)資源沒到,但此時(shí)執(zhí)行shutdownNow(),會(huì)出現(xiàn)什么情況?
    當(dāng)執(zhí)行shutdownNow()方法時(shí),如遇已經(jīng)激活的任務(wù),并且處于阻塞狀態(tài)時(shí),shutdownNow()會(huì)執(zhí)行1次中斷阻塞的操作,此時(shí)對(duì)應(yīng)的線程報(bào)InterruptedException,如果后續(xù)還要等待某個(gè)資源,則按正常邏輯等待某個(gè)資源的到達(dá)。例如,一個(gè)線程正在sleep狀態(tài)中,此時(shí)執(zhí)行shutdownNow(),它向該線程發(fā)起interrupt()請(qǐng)求,而sleep()方法遇到有interrupt()請(qǐng)求時(shí),會(huì)拋出InterruptedException(),并繼續(xù)往下執(zhí)行。在這里要提醒注意的是,在激活的任務(wù)中,如果有多個(gè)sleep(),該方法只會(huì)中斷第一個(gè)sleep(),而后面的仍然按照正常的執(zhí)行邏輯進(jìn)行。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第一部分 來(lái)看一下線程池的框架圖,如下: 1、Executor任務(wù)提交接口與Executors工具類 Execut...
    壓抑的內(nèi)心閱讀 4,390評(píng)論 1 24
  • 前段時(shí)間遇到這樣一個(gè)問(wèn)題,有人問(wèn)微信朋友圈的上傳圖片的功能怎么做才能讓用戶的等待時(shí)間較短,比如說(shuō)一下上傳9張圖片,...
    加油碼農(nóng)閱讀 1,280評(píng)論 0 2
  • 11.3 Android中的線程池 線程池的優(yōu)點(diǎn)可以概括為以下三點(diǎn): 重用線程池中的線程,避免因?yàn)榫€程的創(chuàng)建和銷毀...
    努力生活的西魚閱讀 245評(píng)論 0 0
  • 3.用美好的感覺、信心與目標(biāo)去影響別人 隨著你的行動(dòng)與心態(tài)日漸積極,你就會(huì)慢慢獲得一種美滿人生的感覺,信心日增...
    3af522e9868f閱讀 464評(píng)論 2 4
  • 文/云端一夢(mèng) 女孩是村長(zhǎng)的春天被光陰收買藏在村莊的河流
    云端一夢(mèng)l閱讀 337評(píng)論 0 10

友情鏈接更多精彩內(nèi)容