多線程的三種實現方式以及線程池詳解

1.繼承Thread類;2.Runnable接口。兩種方式都要通過重寫run()方法來定義線程的行爲,推薦使用後者,因爲Java中的繼承是單繼承,一個類有一個父類,如果繼承了Thread類就無法再繼承其他類了,顯然使用Runnable接口更爲靈活。

3.創建線程池

線程池的原理;線程池它裏面的主要參數有哪些;

public ThreadPoolExecutor(int corePoolSize,    //核心線程的數量
                          int maximumPoolSize,    //最大線程數量
                          long keepAliveTime,    //超出核心線程數量以外的線程空餘存活時間
                          TimeUnit unit,    //存活時間的單位
                          BlockingQueue<Runnable> workQueue,    //保存待執行任務的隊列
                          ThreadFactory threadFactory,    //創建新線程使用的工廠
                          RejectedExecutionHandler handler // 當任務無法執行時的處理器
                          ) {...}

線程池的參數有7個,首先根據系統的核數,設置核心線程數(corePoolSize),如果說核心線程數滿了的話,會定義一個阻塞隊列(workQueue),當阻塞隊列滿了的話,會繼續去創建線程,所以這個時候有一個最大的線程數(maximumPoolSize),如果到達最大線程數之後的話,會有一個淘汰策略(handIer),進來之後的話,這個淘汰策略,我們會設置成lfu,還有就是我們線程池去連接的時候,他是有一個連接的超時時間(keepAliveTime)以及超時時間的單位(unit:keepAIiveTime),還有就是我們創建這個線程所用到的一個類加載器(threadFactory)是什麼

keepAliveTime是什麼;

調用線程池如果多長時間沒有繼續再去訪問了,就會把線程回收進來

getTask 怎麼使用 keepAliveTime
(1)首先也是一個自旋,當allowCoreThreadTimeout(運行空閒核心線程超時) 或 wc>corePoolSize(當前線程數量大於核心線程數量) 時,timed會標識爲true,表示需要進行超時判斷。
(2)當wc(當前工作者數量)大於 最大線程數 或 空閒線程的空閒時間大於keepAliveTime(timed && timeout),以及wc>1或(workQueue)任務隊列爲空時,會進入compareAndDecrementWorkerCount方法,對wc的值減1。
(3)當compareAndDecrementWorkerCount方法返回true時,則getTask方法會返回null,終止getTask方法的自旋。這時候回到runWorker方法,就會進入到processWorkerExit方法,進行銷燬worker。

回收的話回收到多少個線程呢;

回收到核心的線程數;

線程池是怎麼實現一個線程複用的;

在線程池的編程模式下,任務是提交給整個線程池,而不是直接提交給某個線程,線程池在拿到任務後,就在內部尋找是否有空閒的線程,如果有,則將任務交給某個空閒的線程。

一個線程同時只能執行一個任務,但可以同時向一個線程池提交多個任務。

使用線程池的好處

 Java中的線程池是運用場景最多的併發框架,幾乎所有需要異步或併發執行任務的程序都可以使用線程池。在開發過程中,合理地使用線程池能夠帶來3個好處:

  第一:降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗。

  第二:提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。

  第三:提高線程的可管理性。線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。但是,要做到合理利用線程池,必須對其實現原理瞭如指掌。

初始化線程池時線程數的選擇

如果任務是IO密集型,一般線程數需要設置2倍CPU數以上,以此來儘量利用CPU資源。

如果任務是CPU密集型,一般線程數量只需要設置CPU數加1即可,更多的線程數也只能增加上下文切換,不能增加CPU利用率。

說說線程池的拒絕策略

    當請求任務不斷的過來,而系統此時又處理不過來的時候,我們需要採取的策略是拒絕服務。RejectedExecutionHandler接口提供了拒絕任務處理的自定義方法的機會。在ThreadPoolExecutor中已經包含四種處理策略。

AbortPolicy策略:該策略會直接拋出異常,阻止系統正常工作。

CallerRunsPolicy 策略:只要線程池未關閉,該策略直接在調用者線程中,運行當前的被丟棄的任務。

DiscardOleddestPolicy策略: 該策略將丟棄最老的一個請求,也就是即將被執行的任務,並嘗試再次提交當前任務。

DiscardPolicy策略:該策略默默的丟棄無法處理的任務,不予任何處理。

    除了JDK默認提供的四種拒絕策略,我們可以根據自己的業務需求去自定義拒絕策略,自定義的方式很簡單,直接實現RejectedExecutionHandler接口即可。

五種線程池的使用場景

newSingleThreadExecutor:一個單線程的線程池,可以用於需要保證順序執行的場景,並且只有一個線程在執行。

newFixedThreadPool:一個固定大小的線程池,可以用於已知併發壓力的情況下,對線程數做限制。

newCachedThreadPool:一個可以無限擴大的線程池,比較適合處理執行時間比較小的任務。

newScheduledThreadPool:可以延時啓動,定時啓動的線程池,適用於需要多個後臺線程執行週期任務的場景。

newWorkStealingPool:一個擁有多個任務隊列的線程池,可以減少連接數,創建當前可用cpu數量的線程來並行執行。

線程池的關閉

關閉線程池可以調用shutdownNow和shutdown兩個方法來實現

shutdownNow:對正在執行的任務全部發出interrupt(),停止執行,對還未開始執行的任務全部取消,並且返回還沒開始的任務列表。

shutdown:當我們調用shutdown後,線程池將不再接受新的任務,但也不會去強制終止已經提交或者正在執行中的任務。

線程池都有哪幾種工作隊列

1、ArrayBlockingQueue

是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。

2、LinkedBlockingQueue

一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列

3、SynchronousQueue

一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。

4、PriorityBlockingQueue

一個具有優先級的無限阻塞隊列。

 

execute和submit的區別?

execute適用於不需要關注返回值的場景,只需要將線程丟到線程池中去執行就可以了。

submit方法適用於需要關注返回值的場景

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章