您好,登錄后才能下訂單哦!
這篇文章主要介紹“使用Java線程池的方法步驟”,在日常操作中,相信很多人在使用Java線程池的方法步驟問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”使用Java線程池的方法步驟”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
1.頻繁創建和銷毀單個線程,浪費資源,并且還會出現頻繁GC
2.缺乏統一管理,各線程相互競爭
ThreadPoolExecutor有四個重載的構造方法,我們這里來說說參數最多的那一個重載的構造方法,這樣大家就知道其他方法參數的含義了,如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
各參數詳細說明:
這里是7個參數(我們在開發中用的更多的是5個參數的構造方法),OK,那我們來看看這里七個參數的含義:
corePoolSize 線程池中核心線程的數量
maximumPoolSize 線程池中最大線程數量
keepAliveTime 非核心線程的超時時長,當系統中非核心線程閑置時間超過keepAliveTime之后,則會被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置為true,則該參數也表示核心線程的超時時長
unit 第三個參數的單位,有納秒、微秒、毫秒、秒、分、時、天等
workQueue 線程池中的任務隊列,該隊列主要用來存儲已經被提交但是尚未執行的任務。存儲在這里的任務是由ThreadPoolExecutor的execute方法提交來的。
threadFactory 為線程池提供創建新線程的功能,這個我們一般使用默認即可
handler 拒絕策略,當線程無法執行新任務時(一般是由于線程池中的線程數量已經達到最大數或者線程池關閉導致的),默認情況下,當線程池無法處理新線程時,會拋出一個RejectedExecutionException。
workQueue介紹
1.ArrayBlockingQueue:這個表示一個規定了大小的BlockingQueue,ArrayBlockingQueue的構造函數接受一個int類型的數據,該數據表示BlockingQueue的大小,存儲在ArrayBlockingQueue中的元素按照FIFO(先進先出)的方式來進行存取。
2.LinkedBlockingQueue:這個表示一個大小不確定的BlockingQueue,在LinkedBlockingQueue的構造方法中可以傳一個int類型的數據,這樣創建出來的LinkedBlockingQueue是有大小的,也可以不傳,不傳的話,LinkedBlockingQueue的大小就為Integer.MAX_VALUE,源碼如下:
3.PriorityBlockingQueue:這個隊列和LinkedBlockingQueue類似,不同的是PriorityBlockingQueue中的元素不是按照FIFO來排序的,而是按照元素的Comparator來決定存取順序的(這個功能也反映了存入PriorityBlockingQueue中的數據必須實現了Comparator接口)。
4.SynchronousQueue:這個是同步Queue,屬于線程安全的BlockingQueue的一種,在SynchronousQueue中,生產者線程的插入操作必須要等待消費者線程的移除操作,Synchronous內部沒有數據緩存空間,因此我們無法對SynchronousQueue進行讀取或者遍歷其中的數據,元素只有在你試圖取走的時候才有可能存在。我們可以理解為生產者和消費者互相等待,等到對方之后然后再一起離開。
拒絕策略
AbortPolicy:直接拒絕,并拋出異常,這也是默認的策略。
CallerRunsPolicy:直接讓調用execute方法的線程去執行此任務。
DiscardOldestPolicy:丟棄最老的未處理的任務,然后重新嘗試執行當前的新任務。
DiscardPolicy:直接丟棄當前任務,但是不拋異常
當線程數量未達到corePoolSize的時候,就會創建新的線程來執行任務。
當核心線程數已滿,就會把任務放到阻塞隊列。
當隊列已滿,并且未達到最大線程數,就會新建非核心線程來執行任務(重要)。
當隊列已滿,并且達到了最大線程數,則選擇一種拒絕策略來執行。
1.FixedThreadPool
固定大小的線程池,可以指定線程池的大小,該線程池corePoolSize和maximumPoolSize相等,阻塞隊列使用的是LinkedBlockingQueue,大小為整數最大值。
該線程池中的線程數量始終不變,當有新任務提交時,線程池中有空閑線程則會立即執行,如果沒有,則會暫存到阻塞隊列。對于固定大小的線程池,不存在線程數量的變化。
同時使用無界的LinkedBlockingQueue來存放執行的任務。當任務提交十分頻繁的時候,LinkedBlockingQueue迅速增大,存在著耗盡系統資源的問題。
而且在線程池空閑時,即線程池中沒有可運行任務時,它也不會釋放工作線程,還會占用一定的系統資源,需要shutdown
2.SingleThreadExecutor
可以看到阻塞隊例 使用的是LinkedBolckingQueue,且默認大小為Integer.MAX_VALUE,這樣的話,如果有大量請求到來,會放入到這個任務隊列里,可能會導致OOM;
3.Executors.newCachedThreadPool()
可緩存線程池,先查看線程池中有沒有以前建立的線程,如果有就直接使用,如果沒有新建一個線程加入線程池中,可緩存線程池
通常用于執行一些生存期很短的異步型任務;線程池為無限大,當執行當前任務時上一個任務已經完成,會復用執行上一個任務的線程,而不用每次新建線程
緩存的線程默認存活60秒。線程的核心池corePoolSize大小為0,核心池最大為Integer.MAX_VALUE,阻塞隊列使用的是SynchronousQueue。
是一個直接提交的阻塞隊列,他總會迫使線程池增加新的線程去執行新的任務。
在沒有任務執行時,當線程的空閑時間超過keepAliveTime(60秒),則工作線程將會終止被回收,當提交新任務時,
如果沒有空閑線程,則創建新線程執行任務,會導致一定的系統開銷。
如果同時又大量任務被提交,而且任務執行的時間不是特別快,那么線程池便會新增出等量的線程池處理任務,這很可能會很快耗盡系統的資源。
4.ScheduledThreadPool
創建一個定長線程池,支持定時及周期性任務執行
定時線程池,該線程池可用于周期性地去執行任務,通常用于周期性的同步數據。
scheduleAtFixedRate:是以固定的頻率去執行任務,周期是指每次執行任務成功執行之間的間隔。
schedultWithFixedDelay:是以固定的延時去執行任務,延時是指上一次執行成功之后和下一次開始執行的之前的時間。
通過上述源碼分析,我們發現newFixedThreadPool和newSingleThreadExecutor方法他們都使用了LinkedBlockingQueue的任務隊列,LinkedBlockingQueue的默認大小為Integer.MAX_VALUE。而newCachedThreadPool中定義的線程池大小為Integer.MAX_VALUE。
所以阿里禁止使用Executors創建線程池的原因就是FixedThreadPool和SingleThreadPool的請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。
CachedThreadPool允許的創建線程數量為Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM。
1.shutDown() 關閉線程池,不影響已經提交的任務
2.shutDownNow() 關閉線程池,并嘗試去終止正在執行的線程
3.allowCoreThreadTimeOut(boolean value) 允許核心線程閑置超時時被回收
4.單例模式創建線程池
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* 異步任務處理器
*/
public class AsyncTaskExecutor {
/** 線程池保持ALIVE狀態線程數 */
public static final int CORE_POOL_SIZE = 10;
/** 線程池最大線程數 */
public static final int MAX_POOL_SIZE = 40;
/** 空閑線程回收時間 */
public static final int KEEP_ALIVE_TIME = 1000;
/** 線程池等待隊列 */
public static final int BLOCKING_QUEUE_SIZE = 1000;
/** 業務請求異步處理線程池 */
private static final ThreadPoolExecutor processExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MICROSECONDS,
new LinkedBlockingQueue<Runnable>(BLOCKING_QUEUE_SIZE),
new TreadFactoryBuilder.setNameFormat("boomoom-thread-pool-%d").build(),
new TreadPoolExecutor.DiscardPolicy());
private AsyncTaskExecutor() {};
/**
* 異步任務處理
*
* @param task 任務
*/
public void execute(Runnable task) {
processExecutor.submit(task);
}
}
懶漢式和饑漢式區別
1.餓漢式是線程安全的,在類創建的同時就已經創建好一個靜態的對象供系統使用,以后不再改變。
2.懶漢式如果想要線程安全必須用雙重檢驗鎖并且對象還必須是volatile,防止對象指令重排
到此,關于“使用Java線程池的方法步驟”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。