日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java:多线程之线程池

發(fā)布時間:2025/6/15 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java:多线程之线程池 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

參考博文:https://www.cnblogs.com/dolphin0520/p/3932921.html

前文講過,使用線程的時候就手動創(chuàng)建并啟動一個線程,使用完后線程被銷毀,這樣就會有一個問題:

如果并發(fā)的線程數(shù)量非常多,并且每個線程都是執(zhí)行一個時間很短的任務(wù)就結(jié)束了,這樣頻繁的創(chuàng)建線程就會大大降低系統(tǒng)的效率,因為頻繁的創(chuàng)建線程和銷毀線程都需要時間。

那么有沒有一種辦法使得線程可以服用呢?就是執(zhí)行完一個任務(wù),并不銷毀,而是可以繼續(xù)執(zhí)行其他任務(wù)。

Java為我們提供了線程池來達到這樣的效果。今天我們就來講解一下Java的線程池。首先我們從最核心的 ThreadPoolExecutor 類中的方法講起,然后給出它的使用示例,最后討論如何合理配置線程池的大小。

一、Java中的ThreadPoolExecutor 類

1、ThreadPoolExecutor

java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類,因此如果要透徹地了解Java中的線程池,必須先了解這個類。下面我們來看一下ThreadPoolExecutor類的具體實現(xiàn)源碼。

(1)繼承關(guān)系與類聲明

java.lang.Object |____java.util.concurrent.AbstractExecutorService|____java.util.concurrent.ThreadPoolExecutorpackage java.util.concurrent; /*** @since 1.5* @author Doug Lea*/ public class ThreadPoolExecutor extends AbstractExecutorService {


(2)構(gòu)造方法

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}


從代碼上看,ThreadPoolExecutor 繼承了 AbstractExecutorService類,并提供了四個構(gòu)造器,事實上,通過觀察每個構(gòu)造器的實現(xiàn),發(fā)現(xiàn)前面三個構(gòu)造器都是調(diào)用的第四個構(gòu)造器進行初始化工作的。

下面解釋一下構(gòu)造器中各個參數(shù)的含義:

corePoolSize:核心池的大小

這個參數(shù)跟后面講述的線程池的實現(xiàn)原理有非常大的關(guān)系。在創(chuàng)建了線程池后,默認情況下,線程池中并沒有任何線程,而是等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù),除非調(diào)用了 prestartAllCoreThreads() 或者 prestartCoreThread() 方法,從這2個方法的名字就可以看出,是預(yù)創(chuàng)建線程的意思,即在沒有任務(wù)到來之前就創(chuàng)建 corePoolSize 個線程或者一個線程。默認情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來之后,才會創(chuàng)建一個線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達到corePoolSize后,就會把到達的任務(wù)放到緩存隊列當(dāng)中。

maximumPoolSize:線程池最大線程數(shù)

這個參數(shù)也是一個非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個線程。

keepAliveTime:空閑線程存活時間

表示線程沒有任務(wù)執(zhí)行時最多保持多久時間會終止。默認情況下,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數(shù)不大于corePoolSize,即當(dāng)線程池中的線程數(shù)大于corePoolSize時,如果一個線程空閑的時間達到keepAliveTime,則會終止,直到線程池中的線程數(shù)不超過 corePoolSize。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)(允許核心線程超時)方法,在線程池中的線程數(shù)不大于corePoolSize時,keepAliveTime參數(shù)也會起作用,直到線程池中的線程數(shù)為0;

unit:時間單位

參數(shù) keepAliveTime 的時間單位,有7種取值,在TimeUnit類中有7種靜態(tài)屬性:

TimeUnit.DAYS; ? ? ? ? ? ? ? //天 TimeUnit.HOURS; ? ? ? ? ? ? //小時 TimeUnit.MINUTES; ? ? ? ? ? //分鐘 TimeUnit.SECONDS; ? ? ? ? ? //秒 TimeUnit.MILLISECONDS; ? ? ?//毫秒 TimeUnit.MICROSECONDS; ? ? ?//微妙 TimeUnit.NANOSECONDS; ? ? ? //納秒


workQueue:阻塞隊列

一個阻塞隊列,用來存儲等待執(zhí)行的任務(wù),這個參數(shù)的選擇也很重要,會對線程池的運行過程產(chǎn)生重大影響,一般來說,這里的阻塞隊列有以下幾種選擇:

ArrayBlockingQueue; PriorityBlockingQueue LinkedBlockingQueue; SynchronousQueue


ArrayBlockingQueue 和 PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和SynchronousQueue。線程池的排隊策略與BlockingQueue有關(guān)。

threadFactory:線程工廠

主要用來創(chuàng)建線程。

handler:拒絕處理策略

表示當(dāng)拒絕處理任務(wù)時的策略,有以下四種取值:

ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。? ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。? ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程) ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)?


2、AbstractExecutorService類

從上面給出的 ThreadPoolExecutor 類的代碼可以知道,ThreadPoolExecutor繼承了AbstractExecutorService,我們來看一下AbstractExecutorService的實現(xiàn):

package java.util.concurrent;/*** @since 1.5* @author Doug Lea*/public abstract class AbstractExecutorService implements ExecutorService {protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {}public Future<?> submit(Runnable task) {}public <T> Future<T> submit(Runnable task, T result) {}public <T> Future<T> submit(Callable<T> task) {}private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,boolean timed, long nanos)throws InterruptedException, ExecutionException, TimeoutException {}public <T> T invokeAny(Collection<? extends Callable<T>> tasks)throws InterruptedException, ExecutionException {}public <T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException {}public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException {}public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException {}}


AbstractExecutorService是一個抽象類,它實現(xiàn)了ExecutorService接口。

3、ExecutorService

我們接著看ExecutorService接口的實現(xiàn):

package java.util.concurrent;/*** @since 1.5* @author Doug Lea*/ public interface ExecutorService extends Executor {void shutdown();List<Runnable> shutdownNow();boolean isShutdown();boolean isTerminated();boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException;<T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException;<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException;<T> T invokeAny(Collection<? extends Callable<T>> tasks)throws InterruptedException, ExecutionException;<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;}


ExecutorService又是繼承了Executor接口,我們看一下Executor接口的實現(xiàn):

4、Executor

Executor接口的實現(xiàn):

?

package java.util.concurrent;/*** @since 1.5* @author Doug Lea*/ public interface Executor {void execute(Runnable command); }


到這里,大家應(yīng)該明白了ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor幾個之間的關(guān)系了。

Executor是一個頂層接口,在它里面只聲明了一個方法execute(Runnable),返回值為void,參數(shù)為Runnable類型,從字面意思可以理解,就是用來執(zhí)行傳進去的任務(wù)的;
然后ExecutorService接口繼承了Executor接口,并聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象類AbstractExecutorService實現(xiàn)了ExecutorService接口,基本實現(xiàn)了ExecutorService中聲明的所有方法;
然后ThreadPoolExecutor繼承了類AbstractExecutorService。


5、ThreadPoolExecutor類中有幾個非常重要的方法:

(1)execute()方法:

方法實際上是Executor中聲明的方法,在ThreadPoolExecutor進行了具體的實現(xiàn),這個方法是ThreadPoolExecutor的核心方法,通過這個方法可以向線程池提交一個任務(wù),交由線程池去執(zhí)行。

(2)submit()方法:

方法是在 ExecutorService 中聲明的方法,在 AbstractExecutorService 就已經(jīng)有了具體的實現(xiàn),在 ThreadPoolExecutor 中并沒有對其進行重寫,這個方法也是用來向線程池提交任務(wù)的,但是它和 execute() 方法不同,它能夠返回任務(wù)執(zhí)行的結(jié)果,去看submit()方法的實現(xiàn),會發(fā)現(xiàn)它實際上還是調(diào)用的execute()方法,只不過它利用了 Future 來獲取任務(wù)執(zhí)行結(jié)果。

(3)shutdown()和shutdownNow()方法:

是用來關(guān)閉線程池的。

(4)很多其他的方法:

比如:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等獲取與線程池相關(guān)屬性的方法,有興趣的朋友可以自行查閱API。

6、使用示例

任務(wù)類:

package basis.stuThreadPool;import java.util.concurrent.TimeUnit;public class MyTask implements Runnable{private int taskNum;public MyTask(int taskNum) {this.taskNum = taskNum;}@Overridepublic void run() {System.out.println("正在執(zhí)行task"+taskNum);try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("task"+taskNum+"執(zhí)行完畢。");} }


?測試類:

package basis.stuThreadPool;import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;public class StuThreadPool {public static void main(String[] args) {/*** 創(chuàng)建一個線程池* 核心線程數(shù):5* 最大線程數(shù):10* 空閑線程 200 毫秒后死亡* 阻塞隊列為:ArrayBlockingQueue類型,容量為 5*/ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,200,?TimeUnit.MICROSECONDS,new ArrayBlockingQueue<Runnable>(5));for (int i = 0;i<15;i++){MyTask myTask = new MyTask(i);executor.execute(myTask);System.out.println("線程池中線程數(shù)目:"+executor.getPoolSize()+",隊列中等待執(zhí)行的任務(wù)數(shù)目:"+executor.getQueue().size()+",已執(zhí)行完的任務(wù)數(shù)目:"+executor.getCompletedTaskCount());}executor.shutdown();} }


執(zhí)行結(jié)果:

正在執(zhí)行task0
線程池中線程數(shù)目:1,隊列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:2,隊列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:3,隊列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:4,隊列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:5,隊列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:5,隊列中等待執(zhí)行的任務(wù)數(shù)目:1,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:5,隊列中等待執(zhí)行的任務(wù)數(shù)目:2,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:5,隊列中等待執(zhí)行的任務(wù)數(shù)目:3,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:5,隊列中等待執(zhí)行的任務(wù)數(shù)目:4,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:5,隊列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:6,隊列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:7,隊列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:8,隊列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:9,隊列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行完的任務(wù)數(shù)目:0
線程池中線程數(shù)目:10,隊列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行完的任務(wù)數(shù)目:0
正在執(zhí)行task1
正在執(zhí)行task2
正在執(zhí)行task3
正在執(zhí)行task4
正在執(zhí)行task10
正在執(zhí)行task11
正在執(zhí)行task12
正在執(zhí)行task13
正在執(zhí)行task14
task0執(zhí)行完畢。
正在執(zhí)行task5
task11執(zhí)行完畢。
task2執(zhí)行完畢。
task4執(zhí)行完畢。
task3執(zhí)行完畢。
task1執(zhí)行完畢。
task10執(zhí)行完畢。
task13執(zhí)行完畢。
task12執(zhí)行完畢。
task14執(zhí)行完畢。
正在執(zhí)行task9
正在執(zhí)行task6
正在執(zhí)行task8
正在執(zhí)行task7
task5執(zhí)行完畢。
task7執(zhí)行完畢。
task8執(zhí)行完畢。
task6執(zhí)行完畢。
task9執(zhí)行完畢。
從執(zhí)行結(jié)果可以看出:

當(dāng)線程池中線程的數(shù)目大于5 (corePoolSize)時,便將任務(wù)放入任務(wù)緩存隊列里面,當(dāng)任務(wù)緩存隊列滿了(capacity = 5)之后,便創(chuàng)建新的線程,線程數(shù)目不能大于 10(maximumPoolSize)的值。該線程池中共創(chuàng)建了10個線程(最開始先創(chuàng)建 5 個,任務(wù)緩存隊列滿了之后再創(chuàng)建 maximumPoolSize-corePoolSize = 5個),另外,任務(wù)緩存隊列中還有 5 個在等待。

如果上面程序中,將for循環(huán)中改成執(zhí)行20個任務(wù),就會拋出任務(wù)拒絕異常了。

不過在java doc中,并不提倡我們直接使用ThreadPoolExecutor,而是使用Executors類來創(chuàng)建線程池。

7、Executors

Executors 是一個線程池工具類,提供了很多靜態(tài)方法,用于簡化線程池的創(chuàng)建和使用。

Executors 中創(chuàng)建線程池的方法:

方法?? ? 描述 newCachedThreadPool()?? ? 創(chuàng)建一個不限容量(最大為Integer.MAX_VALUE)的線程池 newSingleThreadExecutor()?? ? 創(chuàng)建一個單線程的線程池 newFixedThreadExecutor()?? ?創(chuàng)建一個固定容量的線程池


這三個方法的具體實現(xiàn):

public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}


?示例(1)newFixedThreadPool:

任務(wù)類:

package basis.stuThreadPool;import java.util.concurrent.TimeUnit;public class MyTask implements Runnable{private int taskNum;public MyTask(int taskNum) {this.taskNum = taskNum;}@Overridepublic void run() {System.out.println("正在執(zhí)行task"+taskNum);try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("task"+taskNum+"執(zhí)行完畢。");} }


測試類:

package basis.stuThreadPool;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class TestExcutors {public static void main(String[] args) {//創(chuàng)建一個固定大小的線程池ExecutorService executor = Executors.newFixedThreadPool(5);for (int i = 0;i<10;i++){MyTask myTask = new MyTask(i);executor.execute(myTask);}executor.shutdown();} }


測試結(jié)果:

正在執(zhí)行task0
正在執(zhí)行task1
正在執(zhí)行task2
正在執(zhí)行task3
正在執(zhí)行task4
task0執(zhí)行完畢。
正在執(zhí)行task5
task1執(zhí)行完畢。
正在執(zhí)行task6
task3執(zhí)行完畢。
task4執(zhí)行完畢。
task2執(zhí)行完畢。
正在執(zhí)行task8
正在執(zhí)行task7
正在執(zhí)行task9
task5執(zhí)行完畢。
task6執(zhí)行完畢。
task8執(zhí)行完畢。
task7執(zhí)行完畢。
task9執(zhí)行完畢。
上述代碼使用Executors創(chuàng)建了一個固定容量為5的線程池。當(dāng)線程池中線程達到最大容量時,不再添加新的線程,其他線程必須等待線程池中的線程執(zhí)行完成后,才能進入線程池中執(zhí)行。

?示例(2)newCachedThreadPool:

修改測試類,把固定容量的線程池改為不限容量的線程池,

//創(chuàng)建一個不限容量的線程池

ExecutorService executor = Executors.newCachedThreadPool();


其他代碼不變,運行測試,結(jié)果如下:

正在執(zhí)行task0
正在執(zhí)行task1
正在執(zhí)行task2
正在執(zhí)行task3
正在執(zhí)行task4
正在執(zhí)行task5
正在執(zhí)行task6
正在執(zhí)行task7
正在執(zhí)行task8
正在執(zhí)行task9
task0執(zhí)行完畢。
task2執(zhí)行完畢。
task6執(zhí)行完畢。
task4執(zhí)行完畢。
task3執(zhí)行完畢。
task9執(zhí)行完畢。
task1執(zhí)行完畢。
task8執(zhí)行完畢。
task7執(zhí)行完畢。
task5執(zhí)行完畢。
newCachedThreadPool方法會創(chuàng)建一個容量自動調(diào)整的線程池,最大容量為Integer.MAXVALUE。每當(dāng)有新的任務(wù),線程池中就加入一個新的線程。

示例(3)newSingleThreadExecutor:

修改測試類,把不限容量的線程池改為單線程的線程池,

//創(chuàng)建一個單線程的線程池

ExecutorService executor = Executors.newSingleThreadExecutor();


其他代碼不變,運行測試,結(jié)果如下:

正在執(zhí)行task0
task0執(zhí)行完畢。
正在執(zhí)行task1
task1執(zhí)行完畢。
正在執(zhí)行task2
task2執(zhí)行完畢。
正在執(zhí)行task3
task3執(zhí)行完畢。
正在執(zhí)行task4
task4執(zhí)行完畢。
正在執(zhí)行task5
task5執(zhí)行完畢。
正在執(zhí)行task6
task6執(zhí)行完畢。
正在執(zhí)行task7
task7執(zhí)行完畢。
正在執(zhí)行task8
task8執(zhí)行完畢。
正在執(zhí)行task9
task9執(zhí)行完畢。
newSingleThreadExecutor方法會創(chuàng)建一個單線程的線程池,一次只能有一個任務(wù)進入線程池執(zhí)行,其他線程只能等待線程池中的那個線程執(zhí)行完畢后才能進入線程池。

總結(jié)

以上是生活随笔為你收集整理的Java:多线程之线程池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。