您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Java線程池如何創建”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java線程池如何創建”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
可以實現線程的復用,避免重新創建線程和銷毀線程。創建線程和銷毀線程對CPU的開銷是很大的。
可以限制最大可創建的線程數,可根據自己的機器性能動態調整線程池參數,提高應用性能。
提供定時執行、并發數控制等功能。
統一管理線程。
1:緩存線程池(不推薦)
2:固定容量線程池(不推薦)
3:單個線程池(不推薦)
4:定時任務線程池(不推薦)
5:通過ThreadPoolExecutor構造方法創建線程池(阿里巴巴開發手冊十分推薦)
前面4種創建線程池的方式都是通過Executors的靜態方法來創建。
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
為什么不推薦使用緩存線程池?
源碼分析
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
通過上面兩個代碼片段,我們可以看出CachedThreadPool的maximumPoolSize為Integer的最大值2147483647,相當于可以無限的創建線程,而創建線程是需要內存的,這樣就會造成內存溢出,而且一般的機器也沒用那么大的內存給它創建這么大量的線程。
newFixedThreadPool(int num),num就是我們要指定的固定線程數量
ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
輸出:
pool-1-thread-5<thread->run>4
pool-1-thread-4<thread->run>3
pool-1-thread-5<thread->run>5
pool-1-thread-3<thread->run>2
pool-1-thread-3<thread->run>8
pool-1-thread-3<thread->run>9
pool-1-thread-2<thread->run>1
pool-1-thread-1<thread->run>0
pool-1-thread-5<thread->run>7
pool-1-thread-4<thread->run>6
可以看出起到了線程的復用。
為什么FixedThreadPool是固定線程池?
源碼分析
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
通過這個源碼可以看出,核心線程數(corePoolSize)和最大線程數(maximumPoolSize)都為nThreads,因為只有這樣,線程池才不會進行擴容,線程數才固定。
ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
為什么SingleThreadExecutor只含有一個線程?
源碼分析
public static ExecutorService newSingleThreadExecutor() { return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
通過這個源碼可以看出,核心線程數(corePoolSize)和最大線程數(maximumPoolSize)都為1,所以它只含有一個線程。
int initDelay=10; //初始化延時 int period=1;//初始化延遲過了之后,每秒的延時 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"); } },initDelay,period, TimeUnit.SECONDS);
這段代碼的效果是:程序運行之后等10秒,然后輸出第一次結果,之后每隔1秒輸出一次結果。
為什么不推薦使用ScheduledThreadPool?
源碼分析
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue()); }
可以看出ScheduledThreadPool的最大線程數(maximumPoolSize)為Integer的最大值2147483647,相當于可以無限的創建線程,而創建線程是需要內存的,這樣就會造成內存溢出,而且一般的機器也沒用那么大的內存給它創建這么大量的線程。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 12; i++) { final int finalI = i; threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { }
corePoolSize:核心線程數。這些線程一旦被創建不會被銷毀,是一直存在的。線程池默認是沒有線程的,當有任務到來了,就會通過ThreadFactory去創建線程,并一直存在。
maximumPoolSize:最大線程數。非核心線程數=maximumPoolSize-corePoolSize,非核心線程數其實就是可擴容的線程數,可能會被銷毀。
keepAliveTime:非核心線程的空閑存活時間。當通過擴容生成的非核心線程數在keepAliveTime這個時間后還處于空閑狀態,則會銷毀這些非核心線程。
unit:keepAliveTime的時間單位,例如:秒
workQueue:等待區。當來了>corePoolSize的任務時會把任務存放在workQueue這個阻塞隊列中,等待其他線程處理。
threadFactory:線程工廠。創建線程的一種方式。
handler:拒絕策略。當來了>最大線程數+workQueue的容量則會執行拒絕策略
ArrayBlockingQueue:有界阻塞隊列。隊列有大小限制,當容量超過時則會觸發擴容或者拒絕策略。
public ArrayBlockingQueue(int capacity) { this(capacity, false); }
LinkedBlockingQueue:無界阻塞隊列,隊列無大小限制,可能會造成內存溢出。
public LinkedBlockingQueue() { this(2147483647); }
AbortPolicy:直接拋異常
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
DiscardPolicy:不作任何操作。默默丟棄任務
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
DiscardOldestPolicy:丟掉存在時間最長的任務
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
CallerRunsPolicy:讓提交任務的線程去處理任務
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
threadFactory
ThreadFactory threadFactory = Executors.defaultThreadFactory(); threadFactory.newThread(new Runnable() { @Override public void run() { System.out.println("threadFactory"); } }).start();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 26; i++) { //并發數26 final int finalI = i; threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); } /** * 核心線程數=10,最大線程數=20,故可擴容線程數=20-10 * BlockingQueue的大小為5,故等待區的大小為5,也就是當并發數<=核心線程數+5不會擴容,并發數大于16才會擴容 * * 觸發擴容:并發數>核心線程數+阻塞隊列的大小 * 對于這段代碼,如果來了26個并發,10個并發會被核心線程處理,5個會在等待區,剩下11個會因為等待區滿了而觸發擴容 * 因為這里最多能夠擴容10個,這里卻是11個,所以會觸發拒絕策略 */
為什么這段代碼會觸發拒絕策略
對于這段代碼,如果來了26個并發,10個并發會被核心線程處理,5個會在等待區,剩下11個會因為等待區滿了而觸發擴容,但是又因為因為這里最多能夠擴容10個,這里卻是11個,所以會觸發拒絕策略。
怎么觸發擴容
觸發擴容:并發數>核心線程數(corePoolSize)+阻塞隊列(workQueue)的大小
使用Java純手寫一個線程池
讀到這里,這篇“Java線程池如何創建”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。