最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java多線程與線程池技術(shù)分享

 更新時間:2022年04月06日 17:20:27   作者:Java知識圖譜  
這篇文章主要介紹了Java多線程與線程池技術(shù)分享,線程池本質(zhì)是池化技術(shù)的應(yīng)用,和連接池類似,創(chuàng)建連接與關(guān)閉連接屬于耗時操作,下文相關(guān)介紹需要的小伙伴可以參考一下

一、序言

Java多線程編程線程池被廣泛使用,甚至成為了標(biāo)配。

線程池本質(zhì)是池化技術(shù)的應(yīng)用,和連接池類似,創(chuàng)建連接與關(guān)閉連接屬于耗時操作,創(chuàng)建線程與銷毀線程也屬于重操作,為了提高效率,先提前創(chuàng)建好一批線程,當(dāng)有需要使用線程時從線程池取出,用完后放回線程池,這樣避免了頻繁創(chuàng)建與銷毀線程。

// 任務(wù)
Runnable runnable = () -> System.out.println(Thread.currentThread().getId());

在應(yīng)用中優(yōu)先選用線程池執(zhí)行異步任務(wù),根據(jù)不同的場景選用不同的線程池,提高異步任務(wù)執(zhí)行效率。

1、普通執(zhí)行

new Thread(runnable).start();

2、線程池執(zhí)行

Executors.newSingleThreadExecutor().execute(runnable)

二、線程池基礎(chǔ)

1、核心參數(shù)

線程池的核心參數(shù)決定了池的類型,進(jìn)而決定了池的特性。

參數(shù)解釋行為
corePoolSize核心線程數(shù)池中長期維護(hù)的線程數(shù)量,不主動回收
maximumPoolSize最大線程數(shù)最大線程數(shù)大于等于核心線程數(shù)
keepAliveTime線程最大空閑時間非核心線程最大空閑時間,超時回收線程
workQueue工作隊列工作隊列直接決定線程池的類型

2、參數(shù)與池的關(guān)系

Executors類默認(rèn)創(chuàng)建線程池與參數(shù)對應(yīng)關(guān)系。

線程池corePoolSizemaximumPoolSizekeepAliveTimeworkQueue
newCachedThreadPool0Integer.MAX_VALUE60SynchronousQueue
newSingleThreadExecutor110LinkedBlockingQueue
newFixedThreadPoolNN0LinkedBlockingQueue
newScheduledThreadPoolNInteger.MAX_VALUE0DelayedWorkQueue

線程池對比:

根據(jù)使用場景選擇對應(yīng)的線程池。

1、通用對比

線程池特點(diǎn)適用場景
newCachedThreadPool超時未使用的線程回自動銷毀,有新任務(wù)時自動創(chuàng)建適用于低頻、輕量級的任務(wù)。回收線程的目的是節(jié)約線程長時間空閑而占有的資源。
newSingleThreadExecutor線程池中有且只有一個線程順序執(zhí)行任務(wù)
newFixedThreadPool線程池中有固定數(shù)量的線程,且一直存在適用于高頻的任務(wù),即線程在大多數(shù)時間里都處于工作狀態(tài)。
newScheduledThreadPool定時線程池與定時調(diào)度相關(guān)聯(lián)

2、拓展對比

維護(hù)僅有一個線程的線程池有如下兩種方式,正常使用的情況下,二者差異不大;復(fù)雜使用環(huán)境下,二者存在細(xì)微的差異。用newSingleThreadExecutor方式創(chuàng)建的線程池在任何時刻至多只有一個線程,因此可以理解為用異步的方式執(zhí)行順序任務(wù);后者初始化的時候也只有一個線程,使用過程中可能會出現(xiàn)最大線程數(shù)超過1的情況,這時要求線性執(zhí)行的任務(wù)會并行執(zhí)行,業(yè)務(wù)邏輯可能會出現(xiàn)問題,與實際場景有關(guān)。

private final static ExecutorService executor = Executors.newSingleThreadExecutor();
private final static ExecutorService executor = Executors.newFixedThreadPool(1);

線程池原理:

Java多線程與線程池技術(shù)_線程池

線程池主要處理流程,任務(wù)提交之后是怎么執(zhí)行的。大致如下:

  • 判斷核心線程池是否已滿,如果不是,則創(chuàng)建線程執(zhí)行任務(wù)
  • 如果核心線程池滿了,判斷隊列是否滿了,如果隊列沒滿,將任務(wù)放在隊列中
  • 如果隊列滿了,則判斷線程池是否已滿,如果沒滿,創(chuàng)建線程執(zhí)行任務(wù)
  • 如果線程池也滿了,則按照拒絕策略對任務(wù)進(jìn)行處理

提交任務(wù)的方式:

往線程池中提交任務(wù),主要有兩種方法:提交無返回值的任務(wù)和提交有返回值的任務(wù)。

3、無返回值任務(wù)

execute用于提交不需要返回結(jié)果的任務(wù)。

public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.execute(() -> System.out.println("hello"));
}

4、有返回值任務(wù)

submit()用于提交一個需要返回果的任務(wù)。

該方法返回一個Future對象,通過調(diào)用這個對象的get()方法,我們就能獲得返回結(jié)果。get()方法會一直阻塞,直到返回結(jié)果返回。

我們也可以使用它的重載方法get(long timeout, TimeUnit unit),這個方法也會阻塞,但是在超時時間內(nèi)仍然沒有返回結(jié)果時,將拋出異常TimeoutException。

public static void main(String[] args) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    Future<Long> future = executor.submit(() -> {
        System.out.println("task is executed");
        return System.currentTimeMillis();
    });
    System.out.println("task execute time is: " + future.get());
}

在提交任務(wù)時,如果無返回值任務(wù),優(yōu)先使用execute。

關(guān)閉線程池:

在線程池使用完成之后,我們需要對線程池中的資源進(jìn)行釋放操作,這就涉及到關(guān)閉功能。我們可以調(diào)用線程池對象的shutdown()shutdownNow()方法來關(guān)閉線程池。

這兩個方法都是關(guān)閉操作,又有什么不同呢?

  • shutdown()會將線程池狀態(tài)置為SHUTDOWN,不再接受新的任務(wù),同時會等待線程池中已有的任務(wù)執(zhí)行完成再結(jié)束。
  • shutdownNow()會將線程池狀態(tài)置為SHUTDOWN,對所有線程執(zhí)行interrupt()操作,清空隊列,并將隊列中的任務(wù)返回回來。

另外,關(guān)閉線程池涉及到兩個返回boolean的方法,isShutdown()isTerminated,分別表示是否關(guān)閉和是否終止。

三、Executors

Executors是一個線程池工廠,提供了很多的工廠方法,我們來看看它大概能創(chuàng)建哪些線程池。

// 創(chuàng)建單一線程的線程池
public static ExecutorService newSingleThreadExecutor();
// 創(chuàng)建固定數(shù)量的線程池
public static ExecutorService newFixedThreadPool(int nThreads);
// 創(chuàng)建帶緩存的線程池
public static ExecutorService newCachedThreadPool();
// 創(chuàng)建定時調(diào)度的線程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
// 創(chuàng)建流式(fork-join)線程池
public static ExecutorService newWorkStealingPool();

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

任何時候線程池中至多只有一個線程,當(dāng)線程執(zhí)行異常終止時會自動創(chuàng)建一個新線程替換。如果既有異步執(zhí)行任務(wù)的需求又希望任務(wù)得以順序執(zhí)行,那么此類型線程池是首選。

若多個任務(wù)被提交到此線程池,那么會被緩存到隊列。當(dāng)線程空閑的時候,按照FIFO的方式進(jìn)行處理。

2、創(chuàng)建固定數(shù)量的線程池

創(chuàng)建核心線程與最大線程數(shù)相等的固定線程數(shù)的線程池,任何時刻至多有固定數(shù)目的線程,當(dāng)線程因異常而終止時則會自動創(chuàng)建線程替換。

當(dāng)有新任務(wù)加入時,如果池內(nèi)線程均處于活躍狀態(tài),則任務(wù)進(jìn)入等待隊列中,直到有空閑線程,隊列中的任務(wù)才會被順序執(zhí)行;如果池內(nèi)有非活躍線程,則任務(wù)可以立刻得以執(zhí)行。

  • 如果線程的數(shù)量未達(dá)到指定數(shù)量,則創(chuàng)建線程來執(zhí)行任務(wù)
  • 如果線程池的數(shù)量達(dá)到了指定數(shù)量,并且有線程是空閑的,則取出空閑線程執(zhí)行任務(wù)
  • 如果沒有線程是空閑的,則將任務(wù)緩存到隊列(隊列長度為Integer.MAX_VALUE)。當(dāng)線程空閑的時候,按照FIFO的方式進(jìn)行處理

3、創(chuàng)建可伸縮的線程池

這種方式創(chuàng)建的線程池,核心線程池的長度為0,線程池最大長度為Integer.MAX_VALUE。由于本身使用SynchronousQueue作為等待隊列的緣故,導(dǎo)致往隊列里面每插入一個元素,必須等待另一個線程從這個隊列刪除一個元素。

  • 線程池可維護(hù)0到Integer.MAX_VALUE個線程資源,空閑線程默認(rèn)情況下超過60秒未使用則會被銷毀,長期閑置的池占用較少的資源。
  • 當(dāng)有新任務(wù)加入時,如果池中有空閑且尚未銷毀的線程,則將任務(wù)交給此線程執(zhí)行;如果沒有可用的線程,則創(chuàng)建一個新線程執(zhí)行任務(wù)并添加到池中。

4、創(chuàng)建定時調(diào)度的線程池

和上面3個工廠方法返回的線程池類型有所不同,它返回的是ScheduledThreadPoolExecutor類型的線程池。平時我們實現(xiàn)定時調(diào)度功能的時候,可能更多的是使用第三方類庫,比如:quartz等。但是對于更底層的功能,我們?nèi)匀恍枰私狻?/p>

四、手動創(chuàng)建線程池

理論上,我們可以通過Executors來創(chuàng)建線程池,這種方式非常簡單。但正是因為簡單,所以限制了線程池的功能。比如:無長度限制的隊列,可能因為任務(wù)堆積導(dǎo)致OOM,這是非常嚴(yán)重的bug,應(yīng)盡可能地避免。怎么避免?歸根結(jié)底,還是需要我們通過更底層的方式來創(chuàng)建線程池。

拋開定時調(diào)度的線程池不管,我們看看ThreadPoolExecutor。它提供了好幾個構(gòu)造方法,但是最底層的構(gòu)造方法卻只有一個。那么,我們就從這個構(gòu)造方法著手分析。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler);

這個構(gòu)造方法有7個參數(shù),我們逐一來進(jìn)行分析。

  • corePoolSize,線程池中的核心線程數(shù)
  • maximumPoolSize,線程池中的最大線程數(shù)
  • keepAliveTime,空閑時間,當(dāng)線程池數(shù)量超過核心線程數(shù)時,多余的空閑線程存活的時間,即:這些線程多久被銷毀。
  • unit,空閑時間的單位,可以是毫秒、秒、分鐘、小時和天,等等
  • workQueue,等待隊列,線程池中的線程數(shù)超過核心線程數(shù)時,任務(wù)將放在等待隊列,它是一個BlockingQueue類型的對象
  • threadFactory,線程工廠,我們可以使用它來創(chuàng)建一個線程
  • handler,拒絕策略,當(dāng)線程池和等待隊列都滿了之后,需要通過該對象的回調(diào)函數(shù)進(jìn)行回調(diào)處理

這些參數(shù)里面,基本類型的參數(shù)都比較簡單,我們不做進(jìn)一步的分析。我們更關(guān)心的是workQueuethreadFactoryhandler,接下來我們將進(jìn)一步分析。

等待隊列-workQueue

等待隊列是BlockingQueue類型的,理論上只要是它的子類,我們都可以用來作為等待隊列。

同時,jdk內(nèi)部自帶一些阻塞隊列,我們來看看大概有哪些。

  • ArrayBlockingQueue,隊列是有界的,基于數(shù)組實現(xiàn)的阻塞隊列
  • LinkedBlockingQueue,隊列可以有界,也可以無界?;阪湵韺崿F(xiàn)的阻塞隊列
  • SynchronousQueue,不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調(diào)用移除操作,否則插入操作將一直處于阻塞狀態(tài)。該隊列也是Executors.newCachedThreadPool()的默認(rèn)隊列
  • PriorityBlockingQueue,帶優(yōu)先級的無界阻塞隊列

通常情況下,我們需要指定阻塞隊列的上界(比如1024)。另外,如果執(zhí)行的任務(wù)很多,我們可能需要將任務(wù)進(jìn)行分類,然后將不同分類的任務(wù)放到不同的線程池中執(zhí)行。

線程工廠-threadFactory

ThreadFactory是一個接口,只有一個方法。既然是線程工廠,那么我們就可以用它生產(chǎn)一個線程對象。來看看這個接口的定義。

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

Executors的實現(xiàn)使用了默認(rèn)的線程工廠-DefaultThreadFactory。它的實現(xiàn)主要用于創(chuàng)建一個線程,線程的名字為pool-{poolNum}-thread-{threadNum}。

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

很多時候,我們需要自定義線程名字。我們只需要自己實現(xiàn)ThreadFactory,用于創(chuàng)建特定場景的線程即可。

拒絕策略-handler

所謂拒絕策略,就是當(dāng)線程池滿了、隊列也滿了的時候,我們對任務(wù)采取的措施。或者丟棄、或者執(zhí)行、或者其他…

jdk自帶4種拒絕策略,我們來看看。

  • CallerRunsPolicy // 在調(diào)用者線程執(zhí)行
  • AbortPolicy // 直接拋出RejectedExecutionException異常
  • DiscardPolicy // 任務(wù)直接丟棄,不做任何處理
  • DiscardOldestPolicy // 丟棄隊列里最舊的那個任務(wù),再嘗試執(zhí)行當(dāng)前任務(wù)

這四種策略各有優(yōu)劣,比較常用的是DiscardPolicy,但是這種策略有一個弊端就是任務(wù)執(zhí)行的軌跡不會被記錄下來。所以,我們往往需要實現(xiàn)自定義的拒絕策略, 通過實現(xiàn)RejectedExecutionHandler接口的方式。

五、其它

1、配置線程池的參數(shù)

前面我們講到了手動創(chuàng)建線程池涉及到的幾個參數(shù),那么我們要如何設(shè)置這些參數(shù)才算是正確的應(yīng)用呢?實際上,需要根據(jù)任務(wù)的特性來分析。

  • 任務(wù)的性質(zhì):CPU密集型、IO密集型和混雜型
  • 任務(wù)的優(yōu)先級:高中低
  • 任務(wù)執(zhí)行的時間:長中短
  • 任務(wù)的依賴性:是否依賴數(shù)據(jù)庫或者其他系統(tǒng)資源

不同的性質(zhì)的任務(wù),我們采取的配置將有所不同。在《Java并發(fā)編程實踐》中有相應(yīng)的計算公式。

通常來說,如果任務(wù)屬于CPU密集型,那么我們可以將線程池數(shù)量設(shè)置成CPU的個數(shù),以減少線程切換帶來的開銷。如果任務(wù)屬于IO密集型,我們可以將線程池數(shù)量設(shè)置得更多一些,比如CPU個數(shù)*2。

PS:我們可以通過Runtime.getRuntime().availableProcessors()來獲取CPU的個數(shù)。

2、線程池監(jiān)控

如果系統(tǒng)中大量用到了線程池,那么我們有必要對線程池進(jìn)行監(jiān)控。利用監(jiān)控,我們能在問題出現(xiàn)前提前感知到,也可以根據(jù)監(jiān)控信息來定位可能出現(xiàn)的問題。

那么我們可以監(jiān)控哪些信息?又有哪些方法可用于我們的擴(kuò)展支持呢?

首先,ThreadPoolExecutor自帶了一些方法。

  • long getTaskCount(),獲取已經(jīng)執(zhí)行或正在執(zhí)行的任務(wù)數(shù)
  • long getCompletedTaskCount(),獲取已經(jīng)執(zhí)行的任務(wù)數(shù)
  • int getLargestPoolSize(),獲取線程池曾經(jīng)創(chuàng)建過的最大線程數(shù),根據(jù)這個參數(shù),我們可以知道線程池是否滿過
  • int getPoolSize(),獲取線程池線程數(shù)
  • int getActiveCount(),獲取活躍線程數(shù)(正在執(zhí)行任務(wù)的線程數(shù))

其次,ThreadPoolExecutor留給我們自行處理的方法有3個,它在ThreadPoolExecutor中為空實現(xiàn)(也就是什么都不做)。

  • protected void beforeExecute(Thread t, Runnable r) // 任務(wù)執(zhí)行前被調(diào)用
  • protected void afterExecute(Runnable r, Throwable t) // 任務(wù)執(zhí)行后被調(diào)用
  • protected void terminated() // 線程池結(jié)束后被調(diào)用

六、總結(jié)

  • 盡量使用手動的方式創(chuàng)建線程池,避免使用Executors工廠類
  • 根據(jù)場景,合理設(shè)置線程池的各個參數(shù),包括線程池數(shù)量、隊列、線程工廠和拒絕策略

 到此這篇關(guān)于Java十分鐘入門多線程下篇的文章就介紹到這了,更多相關(guān)Java 多線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot + SpringSecurity 短信驗證碼登錄功能實現(xiàn)

    SpringBoot + SpringSecurity 短信驗證碼登錄功能實現(xiàn)

    這篇文章主要介紹了SpringBoot + SpringSecurity 短信驗證碼登錄功能實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Java中常用的設(shè)計模式之觀察者模式詳解

    Java中常用的設(shè)計模式之觀察者模式詳解

    這篇文章主要為大家詳細(xì)介紹了Java中常用的設(shè)計模式之觀察者模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • 基于Java的MathML轉(zhuǎn)圖片的方法(示例代碼)

    基于Java的MathML轉(zhuǎn)圖片的方法(示例代碼)

    最近接到一個新需求mathML轉(zhuǎn)圖片怎么實現(xiàn)呢?剛開始還真是蒙圈了,不知道怎么實現(xiàn),今天小編記錄一種基于Java的MathML轉(zhuǎn)圖片的方法,感興趣的朋友一起看看吧
    2021-06-06
  • ElasticSearch不停機(jī)重建索引延伸思考及優(yōu)化詳解

    ElasticSearch不停機(jī)重建索引延伸思考及優(yōu)化詳解

    這篇文章主要為大家介紹了ElasticSearch不停機(jī)重建索引延伸思考及優(yōu)化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • spring boot整合CAS Client實現(xiàn)單點(diǎn)登陸驗證的示例

    spring boot整合CAS Client實現(xiàn)單點(diǎn)登陸驗證的示例

    本篇文章主要介紹了spring boot整合CAS Client實現(xiàn)單點(diǎn)登陸驗證的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • MyEclipse配置JDK的全過程

    MyEclipse配置JDK的全過程

    這篇文章主要介紹了MyEclipse配置JDK的全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java?鏈表實戰(zhàn)真題訓(xùn)練

    Java?鏈表實戰(zhàn)真題訓(xùn)練

    跟著思路走,之后從簡單題入手,反復(fù)去看,做過之后可能會忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質(zhì)的變化
    2022-04-04
  • RestTemplate集成Ribbbon的示例代碼

    RestTemplate集成Ribbbon的示例代碼

    這篇文章主要介紹了RestTemplate集成Ribbbon的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • Mybatis Plus select 實現(xiàn)只查詢部分字段

    Mybatis Plus select 實現(xiàn)只查詢部分字段

    這篇文章主要介紹了Mybatis Plus select 實現(xiàn)只查詢部分字段的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • maven tomcat plugin實現(xiàn)熱部署

    maven tomcat plugin實現(xiàn)熱部署

    這篇文章主要介紹了maven tomcat plugin實現(xiàn)熱部署,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論

汶川县| 安康市| 玛多县| 姚安县| 湖口县| 勐海县| 南涧| 蓝山县| 达拉特旗| 广昌县| 洛川县| 特克斯县| 新巴尔虎左旗| 吉木萨尔县| 灯塔市| 邵东县| 大关县| 绥江县| 双江| 拉萨市| 大宁县| 东光县| 新宾| 奎屯市| 称多县| 新宁县| 姜堰市| 朝阳市| 贺州市| 鄢陵县| 新巴尔虎左旗| 建平县| 迭部县| 郁南县| 禹州市| 泗洪县| 黔西县| 临颍县| 岗巴县| 通河县| 玛纳斯县|