爲何要使用線程池建立線程?
使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者「過分切換」的問題。
爲何線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 的方式?
Executors 返回的線程池對象的弊端以下:
1) FixedThreadPool 和 SingleThreadPool :
容許的請求隊列長度爲 Integer.MAX_VALUE ,可能會堆積大量的請求,從而致使 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :
容許的建立線程數量爲 Integer.MAX_VALUE ,可能會建立大量的線程,從而致使 OOM 。
以上內容摘自阿里巴巴Java開發手冊-編程規約-併發處理java
下面用一個簡單的例子演示使用ThreadPoolExecutor建立一個線程池編程
public static void main(String[] args){ //建立線程工廠 ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("test-pool-%d").build(); //建立線程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy()); //執行10個線程 for(int i=0;i<10;i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }); } //關閉線程池,不在接受新任務 threadPool.shutdown(); }
咱們先看ThreadPoolExecutor的構造方法,ThreadPoolExecutor提供了三個構造方法,咱們看參數最全的這個:併發
/** * 參數意義: * corePoolSize:核心線程數量,線程池中常存的線程數量,即便這些線程是空閒狀態; * 但若是咱們設置了allowCoreThreadTimeOut這個參數爲true, * 這時核心線程將在空閒超過keepAliveTime後被終止 * maximumPoolSize:最大線程數量,即線程池中容許的最大線程數 * keepAliveTime:多餘線程存活時間,當池中線程數大於核心數時,多餘空閒線程處於空閒狀態的時間不會超過這個時間, * 即多餘的空閒線程超過這個時間就會被終止 * unit:keepAliveTime參數的時間單位 * workQueue:任務隊列,BlockingQueue類型,該隊列用來保存execute方法提交的Runnable任務; * 若是池中沒有空閒的線程是,新任務將會保存到這個隊列中,等待有空閒的線程後再執行。 * threadFactory:線程工廠,用於線程池中建立新線程 * handler:若是線程數量已經最大、任務隊列已經滿了,拒絕新任務的處理策略 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, java.util.concurrent.TimeUnit unit, java.util.concurrent.BlockingQueue<Runnable> workQueue, java.util.concurrent.ThreadFactory threadFactory, java.util.concurrent.RejectedExecutionHandler handler)
下面咱們對workQueue、threadFactory、handler三個參數詳細介紹:
一、workQueue:BlockingQueue類型參數一個阻塞隊列,主要用於緩衝任務;ui
關於阻塞隊列能夠查看個人另外一篇文章:Java阻塞隊列—BlockingQueuegoogle
二、threadFactory:ThreadFactory類型參數,用來告訴線程池怎麼來建立線程
ThreadFactory接口中定義了方法 Thread newThread(Runnable r);經過實現該接口咱們能夠定義接口的名稱、優先級、是否爲守護線程等,咱們能夠本身實現也能夠採用第三方的實現。
咱們參考一下JDK中 Executors類中的DefaultThreadFactory實現,DefaultThreadFactory的源碼以下: .net
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); //線程的優先級設爲默認的5 if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
咱們使用Executors建立的FixedThreadPool、SingleThreadPool、 CachedThreadPool 和 ScheduledThreadPool就是採用的這個DefaultThreadFactory類。
咱們也可用第三方實現的線程建立工廠,如google的ThreadFactoryBuilder線程
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("request-pool-%d") .setPriority(1) .build();
三、 handler:RejectedExecutionHandler類型參數,線程池拒絕新任務的處理策略
在ThreadPoolExecutor中有四個RejectedExecutionHandler的實現,分別是:code
ThreadPoolExecutor經常使用的方法:orm
//讓線程池執行指定的任務,不必定是當即執行 void execute(Runnable command)
//向線程池中提交指定的任務,若是任務執行成功,則返回的Future對象get獲得的將爲null Future<?> submit(Runnable task)
//向線程池中提交指定的任務,若是任務執行成功,返回的Future對象get獲得的爲任務的返回值 <T> Future<T> submit(Callable<T> task)
//向線程池中提交指定的任務,若是任務執行成功,返回的Future對象get獲得的就是入參result <T> Future<T> submit(Runnable task, T result)
//關閉線程池;該方法不會當即終止已經提交的任務,執行該方法後線程池將再也不接收新任務,待池中的全部任務執行完成後纔會真正的關閉線程池 void shutdown()
//阻塞主線程指定的時間;若是池中任務所有完成返回true阻塞終止,若是超時返回false阻塞終止,主線程若是被中斷拋出異常阻塞終止 boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException