偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

寫給小白看的線程池,學(xué)會了嗎?

開發(fā) 后端
線程池(Thread Pool):把一個或多個線程通過統(tǒng)一的方式進(jìn)行調(diào)度和重復(fù)使用的技術(shù),避免了因?yàn)榫€程過多而帶來使用上的開銷。

[[378941]]

為什么要用線程池呢?

下面是一段創(chuàng)建線程并運(yùn)行的代碼:

  1. for (int i = 0; i < 100; i++) { 
  2.     new Thread(() -> { 
  3.         System.out.println("run thread->" + Thread.currentThread().getName()); 
  4.         userService.updateUser(....); 
  5.     }).start(); 

我們想使用這種方式去做異步,或者提高性能,然后將某些耗時操作放入一個新線程去運(yùn)行。

這種思路是沒問題的,但是這段代碼是存在問題的,有哪些問題呢?下面我們就來看看有哪些問題;

  • 創(chuàng)建銷毀線程資源消耗;我們使用線程的目的本是出于效率考慮,可以為了創(chuàng)建這些線程卻消耗了額外的時間,資源,對于線程的銷毀同樣需要系統(tǒng)資源。
  • cpu資源有限,上述代碼創(chuàng)建線程過多,造成有的任務(wù)不能即時完成,響應(yīng)時間過長。
  • 線程無法管理,無節(jié)制地創(chuàng)建線程對于有限的資源來說似乎成了“得不償失”的一種作用。

既然我們上面使用手動創(chuàng)建線程會存在問題,那有解決方法嗎?

答案:有的,使用線程池。

線程池介紹

線程池(Thread Pool):把一個或多個線程通過統(tǒng)一的方式進(jìn)行調(diào)度和重復(fù)使用的技術(shù),避免了因?yàn)榫€程過多而帶來使用上的開銷。

線程池有什么優(yōu)點(diǎn)?

  • 降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
  • 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
  • 提高線程的可管理性。

線程池使用

在JDK中rt.jar包下JUC(java.util.concurrent)創(chuàng)建線程池有兩種方式:ThreadPoolExecutor 和 Executors,其中 Executors又可以創(chuàng)建 6 種不同的線程池類型。

ThreadPoolExecutor 的使用

線程池使用代碼如下:

  1. import java.util.concurrent.LinkedBlockingQueue; 
  2. import java.util.concurrent.ThreadPoolExecutor; 
  3. import java.util.concurrent.TimeUnit; 
  4.  
  5. public class ThreadPoolDemo { 
  6.     private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue(100)); 
  7.  
  8.     public static void main(String[] args) { 
  9.         threadPoolExecutor.execute(new Runnable() { 
  10.             @Override 
  11.             public void run() { 
  12.                 System.out.println("田先生您好"); 
  13.             } 
  14.         }); 
  15.     } 

以上程序執(zhí)行結(jié)果如下:

田先生您好

核心參數(shù)說明

ThreadPoolExecutor的構(gòu)造方法有以下四個:

可以看到最后那個構(gòu)造方法有 7 個構(gòu)造參數(shù),其實(shí)前面的三個構(gòu)造方法只是對最后那個方法進(jìn)行包裝,并且前面三個構(gòu)造方法最終都是調(diào)用最后那個構(gòu)造方法,所以我們這里就來聊聊最后那個構(gòu)造方法。

參數(shù)解釋

corePoolSize

線程池中的核心線程數(shù),默認(rèn)情況下核心線程一直存活在線程池中,如果將 ThreadPoolExecutor 的 allowCoreThreadTimeOut 屬性設(shè)為 true,如果線程池一直閑置并超過了 keepAliveTime 所指定的時間,核心線程就會被終止。

maximumPoolSize

最大線程數(shù),當(dāng)線程不夠時能夠創(chuàng)建的最大線程數(shù)。

keepAliveTime

線程池的閑置超時時間,默認(rèn)情況下對非核心線程生效,如果閑置時間超過這個時間,非核心線程就會被回收。如果 ThreadPoolExecutor 的 allowCoreThreadTimeOut 設(shè)為 true 的時候,核心線程如果超過閑置時長也會被回收。

unit

配合 keepAliveTime 使用,用來標(biāo)識 keepAliveTime 的時間單位。

workQueue

線程池中的任務(wù)隊(duì)列,使用 execute() 或 submit() 方法提交的任務(wù)都會存儲在此隊(duì)列中。

threadFactory

為線程池提供創(chuàng)建新線程的線程工廠。

rejectedExecutionHandler

線程池任務(wù)隊(duì)列超過最大值之后的拒絕策略,RejectedExecutionHandler 是一個接口,里面只有一個 rejectedExecution 方法,可在此方法內(nèi)添加任務(wù)超出最大值的事件處理。ThreadPoolExecutor 也提供了 4 種默認(rèn)的拒絕策略:

  • DiscardPolicy():丟棄掉該任務(wù),不進(jìn)行處理。
  • DiscardOldestPolicy():丟棄隊(duì)列里最近的一個任務(wù),并執(zhí)行當(dāng)前任務(wù)。
  • AbortPolicy():直接拋出 RejectedExecutionException 異常(默認(rèn))。
  • CallerRunsPolicy():既不拋棄任務(wù)也不拋出異常,直接使用主線程來執(zhí)行此任務(wù)。

包含所有參數(shù)的使用案例:

  1. public class ThreadPoolExecutorTest { 
  2.     public static void main(String[] args) throws InterruptedException, ExecutionException { 
  3.         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 
  4.                 10L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(2), 
  5.                 new MyThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); 
  6.         threadPool.allowCoreThreadTimeOut(true); 
  7.         for (int i = 0; i < 10; i++) { 
  8.             threadPool.execute(new Runnable() { 
  9.                 @Override 
  10.                 public void run() { 
  11.                     System.out.println(Thread.currentThread().getName()); 
  12.                     try { 
  13.                         Thread.sleep(2000); 
  14.                     } catch (InterruptedException e) { 
  15.                         e.printStackTrace(); 
  16.                     } 
  17.                 } 
  18.             }); 
  19.         } 
  20.     } 
  21. class MyThreadFactory implements ThreadFactory { 
  22.     private AtomicInteger count = new AtomicInteger(0); 
  23.     @Override 
  24.     public Thread newThread(Runnable r) { 
  25.         Thread t = new Thread(r); 
  26.         String threadName = "MyThread" + count.addAndGet(1); 
  27.         t.setName(threadName); 
  28.         return t; 
  29.     } 

運(yùn)行輸出:

  1. main 
  2. MyThread1 
  3. main 
  4. MyThread1 
  5. MyThread1 
  6. .... 

這里僅僅是為了演示所有參數(shù)自定義,并沒有其他用途。

execute() 和 submit()的使用

execute() 和 submit() 都是用來執(zhí)行線程池的,區(qū)別在于 submit() 方法可以接收線程池執(zhí)行的返回值。

下面分別來看兩個方法的具體使用和區(qū)別:

  1. // 創(chuàng)建線程池 
  2. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue(100)); 
  3. // execute 使用 
  4. threadPoolExecutor.execute(new Runnable() { 
  5.     @Override 
  6.     public void run() { 
  7.         System.out.println("老田您好"); 
  8.     } 
  9. }); 
  10. // submit 使用 
  11. Future<String> future = threadPoolExecutor.submit(new Callable<String>() { 
  12.     @Override 
  13.     public String call() throws Exception { 
  14.         System.out.println("田先生您好"); 
  15.         return "返回值"
  16.     } 
  17. }); 
  18. System.out.println(future.get()); 

以上程序執(zhí)行結(jié)果如下:

  1. 老田您好 
  2. 田先生您好 
  3. 返回值 

Executors

Executors 執(zhí)行器創(chuàng)建線程池很多基本上都是在 ThreadPoolExecutor 構(gòu)造方法上進(jìn)行簡單的封裝,特殊場景根據(jù)需要自行創(chuàng)建。可以把Executors理解成一個工廠類 。Executors可以創(chuàng)建 6 種不同的線程池類型。

下面對這六個方法進(jìn)行簡要的說明:

newFixedThreadPool

創(chuàng)建一個數(shù)量固定的線程池,超出的任務(wù)會在隊(duì)列中等待空閑的線程,可用于控制程序的最大并發(fā)數(shù)。

newCacheThreadPool

短時間內(nèi)處理大量工作的線程池,會根據(jù)任務(wù)數(shù)量產(chǎn)生對應(yīng)的線程,并試圖緩存線程以便重復(fù)使用,如果限制 60 秒沒被使用,則會被移除緩存。如果現(xiàn)有線程沒有可用的,則創(chuàng)建一個新線程并添加到池中,如果有被使用完但是還沒銷毀的線程,就復(fù)用該線程。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閑的線程池不會使用任何資源。

newScheduledThreadPool

創(chuàng)建一個數(shù)量固定的線程池,支持執(zhí)行定時性或周期性任務(wù)。

newWorkStealingPool

Java 8 新增創(chuàng)建線程池的方法,創(chuàng)建時如果不設(shè)置任何參數(shù),則以當(dāng)前機(jī)器CPU 處理器數(shù)作為線程個數(shù),此線程池會并行處理任務(wù),不能保證執(zhí)行順序。

newSingleThreadExecutor

創(chuàng)建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個唯一的線程因?yàn)楫惓=Y(jié)束,那么會有一個新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。

newSingleThreadScheduledExecutor

此線程池就是單線程的 newScheduledThreadPool。

線程池如何關(guān)閉?

線程池關(guān)閉,可以使用 shutdown() 或 shutdownNow() 方法,它們的區(qū)別是:

  • shutdown():不會立即終止線程池,而是要等所有任務(wù)隊(duì)列中的任務(wù)都執(zhí)行完后才會終止。執(zhí)行完 shutdown 方法之后,線程池就不會再接受新任務(wù)了。
  • shutdownNow():執(zhí)行該方法,線程池的狀態(tài)立刻變成 STOP 狀態(tài),并試圖停止所有正在執(zhí)行的線程,不再處理還在池隊(duì)列中等待的任務(wù),執(zhí)行此方法會返回未執(zhí)行的任務(wù)。

下面用代碼來模擬 shutdown() 之后,給線程池添加任務(wù),代碼如下:

  1. import java.util.concurrent.*; 
  2. import java.util.concurrent.atomic.AtomicInteger; 
  3.  
  4. public class ThreadPoolExecutorAllArgsTest { 
  5.    public static void main(String[] args) throws InterruptedException, ExecutionException { 
  6.        //創(chuàng)建線程池 
  7.        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 
  8.                10L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(2), 
  9.                new MyThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); 
  10.        threadPoolExecutor.allowCoreThreadTimeOut(true); 
  11.        //提交任務(wù) 
  12.        threadPoolExecutor.execute(() -> { 
  13.            for (int i = 0; i < 3; i++) { 
  14.                System.out.println("提交任務(wù)" + i); 
  15.                try { 
  16.                    Thread.sleep(3000); 
  17.                } catch (InterruptedException e) { 
  18.                    System.out.println(e.getMessage()); 
  19.                } 
  20.            } 
  21.        }); 
  22.        threadPoolExecutor.shutdown(); 
  23.        //再次提及任務(wù) 
  24.        threadPoolExecutor.execute(() -> { 
  25.            System.out.println("我想再次提及任務(wù)"); 
  26.        }); 
  27.    } 

以上程序執(zhí)行結(jié)果如下:

  1. 提交任務(wù)0 
  2. 提交任務(wù)1 
  3. 提交任務(wù)2 

可以看出,shutdown() 之后就不會再接受新的任務(wù)了,不過之前的任務(wù)會被執(zhí)行完成。

面試題

面試題1:ThreadPoolExecutor 有哪些常用的方法?

  • ThreadPoolExecutor有如下常用方法:
  • submit()/execute():執(zhí)行線程池
  • shutdown()/shutdownNow():終止線程池
  • isShutdown():判斷線程是否終止
  • getActiveCount():正在運(yùn)行的線程數(shù)
  • getCorePoolSize():獲取核心線程數(shù)
  • getMaximumPoolSize():獲取最大線程數(shù)
  • getQueue():獲取線程池中的任務(wù)隊(duì)列
  • allowCoreThreadTimeOut(boolean):設(shè)置空閑時是否回收核心線程

這些方法可以用來終止線程池、線程池監(jiān)控等。

面試題2:說說submit(和 execute兩個方法有什么區(qū)別?

submit() 和 execute() 都是用來執(zhí)行線程池的,只不過使用 execute() 執(zhí)行線程池不能有返回方法,而使用 submit() 可以使用 Future 接收線程池執(zhí)行的返回值。

說說線程池創(chuàng)建需要的那幾個核心參數(shù)的含義

ThreadPoolExecutor 最多包含以下七個參數(shù):

  • corePoolSize:線程池中的核心線程數(shù)
  • maximumPoolSize:線程池中最大線程數(shù)
  • keepAliveTime:閑置超時時間
  • unit:keepAliveTime 超時時間的單位(時/分/秒等)
  • workQueue:線程池中的任務(wù)隊(duì)列
  • threadFactory:為線程池提供創(chuàng)建新線程的線程工廠
  • rejectedExecutionHandler:線程池任務(wù)隊(duì)列超過最大值之后的拒絕策略

面試題3:shutdownNow() 和 shutdown() 兩個方法有什么區(qū)別?

shutdownNow() 和 shutdown() 都是用來終止線程池的,它們的區(qū)別是,使用 shutdown() 程序不會報(bào)錯,也不會立即終止線程,它會等待線程池中的緩存任務(wù)執(zhí)行完之后再退出,執(zhí)行了 shutdown() 之后就不能給線程池添加新任務(wù)了;shutdownNow() 會試圖立馬停止任務(wù),如果線程池中還有緩存任務(wù)正在執(zhí)行,則會拋出 java.lang.InterruptedException: sleep interrupted 異常。

面試題6:了解過線程池的工作原理嗎?

當(dāng)線程池中有任務(wù)需要執(zhí)行時,線程池會判斷如果線程數(shù)量沒有超過核心數(shù)量就會新建線程池進(jìn)行任務(wù)執(zhí)行,如果線程池中的線程數(shù)量已經(jīng)超過核心線程數(shù),這時候任務(wù)就會被放入任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行;如果任務(wù)隊(duì)列超過最大隊(duì)列數(shù),并且線程池沒有達(dá)到最大線程數(shù),就會新建線程來執(zhí)行任務(wù);如果超過了最大線程數(shù),就會執(zhí)行拒絕執(zhí)行策略。

面試題5:線程池中核心線程數(shù)量大小怎么設(shè)置?

「CPU密集型任務(wù)」:比如像加解密,壓縮、計(jì)算等一系列需要大量耗費(fèi) CPU 資源的任務(wù),大部分場景下都是純 CPU 計(jì)算。盡量使用較小的線程池,一般為CPU核心數(shù)+1。因?yàn)镃PU密集型任務(wù)使得CPU使用率很高,若開過多的線程數(shù),會造成CPU過度切換。

「IO密集型任務(wù)」:比如像 MySQL 數(shù)據(jù)庫、文件的讀寫、網(wǎng)絡(luò)通信等任務(wù),這類任務(wù)不會特別消耗 CPU 資源,但是 IO 操作比較耗時,會占用比較多時間??梢允褂蒙源蟮木€程池,一般為2*CPU核心數(shù)。IO密集型任務(wù)CPU使用率并不高,因此可以讓CPU在等待IO的時候有其他線程去處理別的任務(wù),充分利用CPU時間。

另外:線程的平均工作時間所占比例越高,就需要越少的線程;線程的平均等待時間所占比例越高,就需要越多的線程;

以上只是理論值,實(shí)際項(xiàng)目中建議在本地或者測試環(huán)境進(jìn)行多次調(diào)優(yōu),找到相對理想的值大小。

面試題7:線程池為什么需要使用(阻塞)隊(duì)列?

主要有三點(diǎn):

  • 因?yàn)榫€程若是無限制的創(chuàng)建,可能會導(dǎo)致內(nèi)存占用過多而產(chǎn)生OOM,并且會造成cpu過度切換。
  • 創(chuàng)建線程池的消耗較高。

面試題8:線程池為什么要使用阻塞隊(duì)列而不使用非阻塞隊(duì)列?

阻塞隊(duì)列可以保證任務(wù)隊(duì)列中沒有任務(wù)時阻塞獲取任務(wù)的線程,使得線程進(jìn)入wait狀態(tài),釋放cpu資源。

當(dāng)隊(duì)列中有任務(wù)時才喚醒對應(yīng)線程從隊(duì)列中取出消息進(jìn)行執(zhí)行。

使得在線程不至于一直占用cpu資源。

(線程執(zhí)行完任務(wù)后通過循環(huán)再次從任務(wù)隊(duì)列中取出任務(wù)進(jìn)行執(zhí)行,代碼片段如下

  1. while (task != null || (task = getTask()) != null) {})。 

不用阻塞隊(duì)列也是可以的,不過實(shí)現(xiàn)起來比較麻煩而已,有好用的為啥不用呢?

面試題9:了解線程池狀態(tài)嗎?

通過獲取線程池狀態(tài),可以判斷線程池是否是運(yùn)行狀態(tài)、可否添加新的任務(wù)以及優(yōu)雅地關(guān)閉線程池等。

RUNNING:線程池的初始化狀態(tài),可以添加待執(zhí)行的任務(wù)。

SHUTDOWN:線程池處于待關(guān)閉狀態(tài),不接收新任務(wù)僅處理已經(jīng)接收的任務(wù)。

STOP:線程池立即關(guān)閉,不接收新的任務(wù),放棄緩存隊(duì)列中的任務(wù)并且中斷正在處理的任務(wù)。

TIDYING:線程池自主整理狀態(tài),調(diào)用 terminated() 方法進(jìn)行線程池整理。

TERMINATED:線程池終止?fàn)顟B(tài)。

面試題10:知道線程池中線程復(fù)用原理嗎?

線程池將線程和任務(wù)進(jìn)行解耦,線程是線程,任務(wù)是任務(wù),擺脫了之前通過 Thread 創(chuàng)建線程時的一個線程必須對應(yīng)一個任務(wù)的限制。

在線程池中,同一個線程可以從阻塞隊(duì)列中不斷獲取新任務(wù)來執(zhí)行,其核心原理在于線程池對 Thread 進(jìn)行了封裝,并不是每次執(zhí)行任務(wù)都會調(diào)用 Thread.start() 來創(chuàng)建新線程,而是讓每個線程去執(zhí)行一個“循環(huán)任務(wù)”,在這個“循環(huán)任務(wù)”中不停的檢查是否有任務(wù)需要被執(zhí)行,如果有則直接執(zhí)行,也就是調(diào)用任務(wù)中的 run 方法,將 run 方法當(dāng)成一個普通的方法執(zhí)行,通過這種方式將只使用固定的線程就將所有任務(wù)的 run 方法串聯(lián)起來。

總結(jié)

本文通過沒有使用線程池帶來的弊端,Executors介紹,Executors的六種方法介紹、如何使用線程池,了解線程池原理,核心參數(shù),以及10到線程池面試題。

本文轉(zhuǎn)載自微信公眾號「Java后端技術(shù)全棧」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Java后端技術(shù)全棧公眾號。

 

責(zé)任編輯:武曉燕 來源: Java后端技術(shù)全棧
相關(guān)推薦

2021-06-25 07:37:33

遞歸函數(shù)算法

2020-08-11 11:40:31

線程進(jìn)程Linux

2023-10-30 11:40:36

OOM線程池單線程

2023-05-04 10:08:00

Windows 10WinAFL二進(jìn)制

2023-01-28 09:50:17

java多線程代碼

2022-08-08 10:45:49

PromiseJava腳手架

2023-12-11 08:03:01

Java線程線程組

2021-10-04 09:29:41

對象池線程池

2022-07-08 09:27:48

CSSIFC模型

2024-01-19 08:25:38

死鎖Java通信

2024-02-04 00:00:00

Effect數(shù)據(jù)組件

2023-07-26 13:11:21

ChatGPT平臺工具

2023-01-10 08:43:15

定義DDD架構(gòu)

2025-01-27 00:00:00

線程安全Java

2024-02-02 11:03:11

React數(shù)據(jù)Ref

2023-10-10 11:04:11

Rust難點(diǎn)內(nèi)存

2024-05-06 00:00:00

InnoDBView隔離

2023-01-30 09:01:54

圖表指南圖形化

2024-07-31 08:39:45

Git命令暫存區(qū)

2023-12-12 08:02:10

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號