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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java线程池原理及使用

發布時間:2025/3/20 java 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java线程池原理及使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

java中的線程池是運用場景最多的并發框架。在開發過程中,合理的使用線程池能夠帶來下面的一些好處:
1、降低資源的消耗。
2、提高響應速度。
3、提高線程的可管理型。

1.1、線程池ThreadPoolExecutor工作原理

講解之前,我們先看一張原理圖

ThreadPoolExecutor執行execute方法有4種情況:
1)如果當前運行的線程少于corePoolSize,則創建新的線程來執行任務。
2)如果運行的線程等于或者多余corePoolSize,則將任務加入BlockingQueue中,在等待隊列中,等待有新的線程可以運行。
3)如果BlockingQueue隊列滿了,且沒有超過maxPoolSize,則創建新的線程來處理任務。
4)如果創建的線程超過maxPoolSize,任務會拒絕,并調用RejectExecutionHandler.rejectedExecution()方法。

1.2、線程池的使用

1.2.1、線程池的創建

一般我們可以通過ThreadPoolExecutor來創建一個線程池。
在ThreadPoolExecutor類中提供了四個構造方法:

public class ThreadPoolExecutor extends AbstractExecutorService {.....public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);... }

我們通過

new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);

創建一個新的線程池。

下面我們介紹一下需要輸入的幾個參數的意義:

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

2) maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程;

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

  • unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:
TimeUnit.DAYS; //天 TimeUnit.HOURS; //小時 TimeUnit.MINUTES; //分鐘 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //納秒</pre>

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

  • ArrayBlockingQueue:一個基于數組結構的有界阻塞隊列。
  • LinkedBlockingQueue:一個基于鏈表的阻塞隊列,吞吐量要高于ArrayBlockingQueue。
  • SynchronousQueue:一個不存儲元素的阻塞隊列。每次插入操作必須等到另外一個線程調用移除操作,否則一直處于阻塞狀態。吞吐量要高于LinkedBlockingQueue。
  • PriorityBlockingQueue:一個具有優先級的無線阻塞隊列。

ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。

5)threadFactory:線程工廠,主要用來創建線程;
6)RejectedExecutionHandler:當隊列和線程池都滿了,將會執行下面的策略,jdk1.5中提供有以下四種策略:

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

1.2.2、如何向線程池提交任務

向線程池提交任務,提供了兩種方法,分別是execute()和submit()方法。

1)execute()方法

execute方法用于提交不需要返回值的任務,所以也就意味著無法判斷是否執行成功。

pool.execute(new Runnable(){@Overridepublic void run() {System.out.println("使用execute提交任務.");}});

2)submit方法

submit方法可以用于提交需要有返回值的任務。線程池會返回一個future類型的對象,通過這個future對象可以判讀是否執行成功,并且還可以通過get()方法來獲取返回值。

Runnable task = null;Future<Object> future = (Future<Object>) pool.submit(task);try {future.get();//獲取返回值} catch (InterruptedException e) {//中斷異常處理// TODO Auto-generated catch blocke.printStackTrace();} catch (ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {//關閉線程池pool.shutdown();}

1.2.3、關閉線程池

在上一節中,我們在異常的處理后面,我們就使用到了shutdown()方法來關閉線程池。

在關閉線程池的時候,這里有兩個方法可以調用,分別是shutdown和shutdownNow方法。

1.3、線程池使用實例

1.3.1、線程池的使用實例

這個實例我們使用自定義的拒絕策略,因為jdk的策略并不是很完美

public class MyRejected implements RejectedExecutionHandler{public MyRejected(){}@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("自定義處理..");System.out.println("當前被拒絕任務為:" + r.toString());}}

然后我們定義一個任務類

public class MyTask implements Runnable {private int taskId;private String taskName;public MyTask(int taskId, String taskName){this.taskId = taskId;this.taskName = taskName;}public int getTaskId() {return taskId;}public void setTaskId(int taskId) {this.taskId = taskId;}public String getTaskName() {return taskName;}public void setTaskName(String taskName) {this.taskName = taskName;}@Overridepublic void run() {try {System.out.println("run taskId =" + this.taskId);Thread.sleep(5*1000);//System.out.println("end taskId =" + this.taskId);} catch (InterruptedException e) {e.printStackTrace();} }public String toString(){return Integer.toString(this.taskId);}}

最后,我們看一下任務執行

public class UseThreadPoolExecutor1 {public static void main(String[] args) {/*** 在使用有界隊列時,若有新的任務需要執行,如果線程池實際線程數小于corePoolSize,則優先創建線程,* 若大于corePoolSize,則會將任務加入隊列,* 若隊列已滿,則在總線程數不大于maximumPoolSize的前提下,創建新的線程,* 若線程數大于maximumPoolSize,則執行拒絕策略。或其他自定義方式。* */ ThreadPoolExecutor pool = new ThreadPoolExecutor(1, //coreSize2, //MaxSize60, //60TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3) //指定一種隊列 (有界隊列)//new LinkedBlockingQueue<Runnable>(), new MyRejected()//, new DiscardOldestPolicy());MyTask mt1 = new MyTask(1, "任務1");MyTask mt2 = new MyTask(2, "任務2");MyTask mt3 = new MyTask(3, "任務3");MyTask mt4 = new MyTask(4, "任務4");MyTask mt5 = new MyTask(5, "任務5");MyTask mt6 = new MyTask(6, "任務6");pool.execute(mt1);pool.execute(mt2);pool.execute(mt3);/*pool.execute(mt4);pool.execute(mt5);pool.execute(mt6);*/pool.shutdown(); // pool.shutdownNow();} }

執行結果:
1)當運行<5個時,可以正常運行:

2)當>5時,因為大于了最大值,所以執行了異常策略:

1.3.2、線程池的監控參數或者其他api使用

當我們需要對線程池進行監控時,我們可以使用線程池提供的參數進行監控,可以使用下面的一些屬性。

  • taskCount:線程池需要執行的任務數量。
  • completedTaskCount:線程池在運行過程中已完成的數量。
  • largestPoolSize:線程池里曾經創建過的最大的線程數量。
  • poolSize:線程池的線程數量。
  • ActiveCount:獲取活動的線程數量。
System.out.println(pool.getTaskCount()); System.out.println(pool.getCompletedTaskCount()); System.out.println(pool.getLargestPoolSize()); System.out.println(pool.getPoolSize()); System.out.println(pool.getActiveCount());

運行結果:

1.4、如何合理的配置線程池的大小

一般需要根據任務的類型來配置線程池大小:

  • 如果是CPU密集型任務,就需要盡量壓榨CPU,參考值可以設為 NCPU+1

  • 如果是IO密集型任務,參考值可以設置為2*NCPU

  • 建議使用有界隊列。因為有界隊列能夠增加系統的穩定性和預警的能力,我們可以想象一下,當我們使用無界隊列的時候,當我們系統的后臺的線程池的隊列和線程池會越來越多,這樣當達到一定的程度的時候,有可能會撐滿內存,導致系統出現問題。當我們是有界隊列的時候,當我們系統的后臺的線程池的隊列和線程池滿了之后,會不斷的拋出異常的任務,我們可以通過異常信息做一些事情。

當然,這只是一個參考值,具體的設置還需要根據實際情況進行調整,比如可以先將線程池大小設置為參考值,再觀察任務運行情況和系統負載、資源利用率來進行適當調整。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的Java线程池原理及使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。