亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中阻塞隊列和線程池的示例分析

發布時間:2021-09-05 18:16:52 來源:億速云 閱讀:181 作者:小新 欄目:開發技術

小編給大家分享一下Java中阻塞隊列和線程池的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

【1】阻塞隊列

一、什么是阻塞隊列?

① 支持阻塞的插入方法:意思是當隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿。

② 支持阻塞的移除方法:意思是在隊列為空時,獲取元素的線程會等待隊列變為非空。

在并發編程中使用生產者和消費者模式能夠解決絕大多數并發問題。該模式通過平衡生產線程和消費線程的工作能力來提高程序整體處理數據的速度。

在線程世界里,生產者就是生產數據的線程,消費者就是消費數據的線程。在多線程開發中,如果生產者處理速度很快,而消費者處理速度很慢,那么生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大于生產者,那么消費者就必須等待生產者。

為了解決這種生產消費能力不均衡的問題,便有了生產者和消費者模式。生產者和消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通信,而是通過阻塞隊列來進行通信,所以生產者生產完數據之后不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列里取,阻塞隊列就相當于一個緩沖區,平衡了生產者和消費者的處理能力。

阻塞隊列常用于生產者和消費者的場景,生產者是向隊列里添加元素的線程,消費者是從隊列里取元素的線程。阻塞隊列就是生產者用來存放元素、消費者用來獲取元素的容器。

在Android開發中阻塞隊列也是常見的 —— Handler機制中的MessageQueue就是優先級阻塞隊列

Java中阻塞隊列和線程池的示例分析

二、阻塞隊列有什么用?

解耦 在生產者和消費者之間解除了耦合

平衡兩者性能差異 平衡了生產者消費者之間的性能差異

Java中阻塞隊列和線程池的示例分析

三、阻塞隊列的簡單實用

①常見的阻塞隊列主要有那些?

常見的阻塞隊列主要有一下7中:


Java中阻塞隊列和線程池的示例分析

  • ArrayBlockingQueue

  • LinkedBlockingQueue

  • PriorityBlockingQueue

  • DelayQueue

  • SynchronousQueue

  • LinkedTransferQueue

  • LinkedBlockingDeque

②阻塞隊列常見的幾種處理方式(并非所有方式都阻塞)

方法\處理方式拋出異常返回特殊值一直阻塞超時退出
插入方法add(e)offer(e)put(e)offer(e,time,unit)
移除方法remove()poll()take()poll(time,unit)
檢查方法element()peek()不可用不可用
  • 其中只有put和take方法時阻塞的

  • 拋出異常:當隊列滿時,如果再往隊列里插入元素,會拋出IllegalStateException(“Queuefull”)異常。當隊列空時,從隊列里獲取元素會拋出NoSuchElementException異常。

  • -返回特殊值:當往隊列插入元素時,會返回元素是否插入成功,成功返回true。如果是移除方法,則是從隊列里取出一個元素,如果沒有則返回null。

  • 一直阻塞:當阻塞隊列滿時,如果生產者線程往隊列里put元素,隊列會一直阻塞生產者線程,直到隊列可用或者響應中斷退出。當隊列空時,如果消費者線程從隊列里take元素,隊列會阻塞住消費者線程,直到隊列不為空。

  • 超時退出:當阻塞隊列滿時,如果生產者線程往隊列里插入元素,隊列會阻塞生產者線程一段時間,如果超過了指定的時間,生產者線程就會退出。

③阻塞隊列簡單使用

  • 三個線程添加數據

  • 三個線程消費數據

public class MyBlockingQueue {
    static ArrayBlockingQueue<String> abq = new ArrayBlockingQueue(3);
    public static void main(String[] args) {
        // 生產者線程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> producer(), "producerThread" + i).start();
        }
        // 消費者線程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> consumer(), "consumerThread" + i).start();
        }
    }

    private static void consumer() {
        while (true) {
            try {
                String msg = abq.take();
                System.out.println(Thread.currentThread().getName() + " ->receive msg:" + msg);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void producer() {
        for (int i = 0; i < 100; i++) {
            try {
                abq.put("[" + i + "]");
                System.out.println(Thread.currentThread().getName() + " ->send msg:" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

執行結果:

producerThread1 ->send msg:0
producerThread2 ->send msg:0
producerThread0 ->send msg:0
consumerThread1 ->receive msg:[0]
producerThread1 ->send msg:1
consumerThread2 ->receive msg:[0]
producerThread1 ->send msg:2
producerThread2 ->send msg:1
consumerThread1 ->receive msg:[0]
consumerThread0 ->receive msg:[1]
...

【2】Java 線程池

一、我們為什么需要Java 線程池?使用它的好處是什么?

①降低資源消耗。

通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。

②提高響應速度。

通常我們在Java程序中執行一個任務的到結果分為以下步驟:

1.創建線程 ——> 2.執行任務 ——> 3.銷毀線程

當任務到達時,任務可以不需要等到線程創建就能立即執行。假設一個服務器完成一項任務所需時間為:T1 創建線程時間,T2 在線程中執行任務的時間,T3 銷毀線程時間。 如果:T1 + T3 遠大于 T2,則可以采用線程池,以提高服務器性能。線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啟動和結束的時間段或者一些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。

③提高線程的可管理性。

線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。

二、Java中主要提供了哪幾種線程的線程池?

Java中主要提供了一下4中線程池:

Java中阻塞隊列和線程池的示例分析

1、newCachedThreadPool:用來創建一個可以無限擴大的線程池,適用于負載較輕的場景,執行短期異步任務。(可以使得任務快速得到執行,因為任務時間執行短,可以很快結束,也不會造成cpu過度切換)

2、newFixedThreadPool:創建一個固定大小的線程池,因為采用無界的阻塞隊列,所以實際線程數量永遠不會變化,適用于負載較重的場景,對當前線程數量進行限制。(保證線程數可控,不會造成線程過多,導致系統負載更為嚴重)

3、newSingleThreadExecutor:創建一個單線程的線程池,適用于需要保證順序執行各個任務。

4、newScheduledThreadPool:適用于執行延時或者周期性任務。

三、線程類的繼承關系

  • ThreadPoolExecutor 的類關系

  • Executor是一個接口,它是Executor框架的基礎,它將任務的提交與任務的執行分離開來。

  • ExecutorService接口繼承了Executor,在其上做了一些shutdown()、submit()的擴展,可以說是真正的線程池接口;

  • AbstractExecutorService抽象類實現了ExecutorService接口中的大部分方法;

  • ThreadPoolExecutor是線程池的核心實現類,用來執行被提交的任務。

  • ScheduledExecutorService接口繼承了ExecutorService接口,提供了帶"周期執行"功能ExecutorService;

  • ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲后運行命令,或者定期執行命令。ScheduledThreadPoolExecutor比Timer更靈活,功能更強大。

Executor——>ExecutorService——>AbstractExecutorService——>ThreadPoolExecutor

二中常用的幾種線程池都是源自ThreadPoolExecutor,所以我們來分析一下這個類

四、ThreadPoolExecutor參數的含義 corePoolSize

  • corePoolSize

線程池中的核心線程數,當提交一個任務時,線程池創建一個新線程執行任務,直到當前線程數等于corePoolSize;如果當前線程數為corePoolSize,繼續提交的任務被保存到阻塞隊列中,等待被執行;

如果執行了線程池的prestartAllCoreThreads()方法,線程池會提前創建并啟動所有核心線程。

  • maximumPoolSize

線程池中允許的最大線程數。如果當前阻塞隊列滿了,且繼續提交任務,則創建新的線程執行任務,前提是當前線程數小于maximumPoolSize

  • keepAliveTime

線程空閑時的存活時間,即當線程沒有任務執行時,繼續存活的時間。默認情況下,該參數只在線程數大于corePoolSize時才有用

  • TimeUnit

keepAliveTime的時間單位

  • workQueue

workQueue必須是BlockingQueue阻塞隊列。當線程池中的線程數超過它的corePoolSize的時候,線程會進入阻塞隊列進行阻塞等待。通過workQueue,線程池實現了阻塞功能。

一般來說,我們應該盡量使用有界隊列,因為使用無界隊列作為工作隊列會對線程池帶來如下影響。

1)當線程池中的線程數達到corePoolSize后,新任務將在無界隊列中等待,因此線程池中的線程數不會超過corePoolSize。

2)由于1,使用無界隊列時maximumPoolSize將是一個無效參數。

3)由于1和2,使用無界隊列時keepAliveTime將是一個無效參數。

4)更重要的,使用無界queue可能會耗盡系統資源,有界隊列則有助于防止資源耗盡,同時即使使用有界隊列,也要盡量控制隊列的大小在一個合適的范圍。

  • threadFactory

創建線程的工廠,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名,當然還可以更加自由的對線程做更多的設置,比如設置所有的線程為守護線程。

Executors靜態工廠里默認的threadFactory,線程的命名規則是“pool-數字-thread-數字”。

  • RejectedExecutionHandler

線程池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續提交任務,必須采取一種策略處理該任務,線程池提供了4種策略:

(1)AbortPolicy:直接拋出異常,默認策略;

(2)CallerRunsPolicy:用調用者所在的線程來執行任務;

(3)DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,并執行當前任務;

(4)DiscardPolicy:直接丟棄任務;

當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日志或持久化存儲不能處理的任務。

五、線程池工作流程(機制)

Java中阻塞隊列和線程池的示例分析

1. 如果當前運行的線程少于corePoolSize,則創建新線程來執行任務(注意,執行這一步驟需要獲取全局鎖)。

2. 如果運行的線程等于或多于corePoolSize,則將任務加入BlockingQueue。

3. 如果無法將任務加入BlockingQueue(隊列已滿),則創建新的線程來處理任務。

4. 如果創建新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,并調用RejectedExecutionHandler.rejectedExecution()方法。

六、關于兩種提交方法的比較

execute()方法用于提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。

submit()方法用于提交需要返回值的任務。線程池會返回一個future類型的對象,通過這個future對象可以判斷任務是否執行成功,并且可以通過future的get()方法來獲取返回值,get()方法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間后立即返回,這時候有可能任務沒有執行完。

以上是“Java中阻塞隊列和線程池的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

望城县| 敦煌市| 鸡东县| 五华县| 磴口县| 延川县| 连平县| 介休市| 伊川县| 龙陵县| 紫阳县| 赣榆县| 漳州市| 遂平县| 陆良县| 禄劝| 台北县| 峨边| 唐海县| 偏关县| 商洛市| 富宁县| 大姚县| 札达县| 彭州市| 双牌县| 满城县| 射洪县| 新昌县| 清原| 浦县| 洛浦县| 磐安县| 紫金县| 太白县| 绍兴县| 蒲江县| 平山县| 昌图县| 宜兴市| 孟州市|