線程池這個概念已經深入人心了,今天就是通過幾個入門事例,學習一下線程池在JAVA中的應用。
一、大小固定的線程池——Executors.newFixedThreadPool()
下面咱們明確兩個類:
1、ExecutorService:線程池的接口類
2、Executors:Java里面線程池的頂級接口是Executor,但是嚴格意義上講Executor並不是一個線程池,而只是一個執行線程的工具
3、Executors.newFixedThreadPool():這是一個靜態方法,也是這個事例的核心,目的是創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大值。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。
package com.taobao.threadpool; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; /** * 線程池的應用 * @author liqiu * */ public class Test { public static void main(String[] args) { // 創建一個可重用固定線程數的線程池 ExecutorService pool = Executors.newFixedThreadPool(3); // 將線程放入池中進行執行 for (int i=0; i<9; i++){ pool.execute(new MyThread(i)); } // 關閉線程池 pool.shutdown(); } } class MyThread extends Thread { int num; public MyThread(int i){ this.num = i; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "正在執行。。。" + this.num); } }
執行的結果:
pool-1-thread-1正在執行。。。0 pool-1-thread-1正在執行。。。3 pool-1-thread-1正在執行。。。4 pool-1-thread-3正在執行。。。2 pool-1-thread-3正在執行。。。6 pool-1-thread-3正在執行。。。7 pool-1-thread-3正在執行。。。8 pool-1-thread-2正在執行。。。1 pool-1-thread-1正在執行。。。5
二、其他的Executores方法
1、Executors.newSingleThreadExecutor():創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。這種用法我認為違背了線程池的概念,畢竟是一個池子,竟然只有一條魚,很沒勁。事例和上一個一樣,有興趣的話自己試一試。
2、Executors.newCachedThreadPool():創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那么就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。說白了就是默認有若干個線程,如果請求增多了,也會繼續增加線程的個數,但是會自動自動回收超出的部分線程。
3、Executors.newScheduledThreadPool():創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。
4、Executors.newSingleThreadExecutor():創建一個單線程的線程池。此線程池支持定時以及周期性執行任務的需求。
三、自定義線程池
閱讀了上面的內容,應該已經可以使用JAVA提供的線程池了,但是有一個特殊的情況還是需要咱們特制線程池。
package com.taobao.threadpool; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 線程池的應用 * * @author liqiu * */ public class Test { public static void main(String[] args) { // 創建等待隊列 BlockingQueue bqueue = new ArrayBlockingQueue(20); // 創建一個單線程執行程序,它可安排在給定延遲后運行命令或者定期地執行。 ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 2, TimeUnit.MILLISECONDS, bqueue); // 創建實現了Runnable接口對象,Thread對象當然也實現了Runnable接口 for (int i = 0; i <= 9; i++) { // 將線程放入池中進行執行 pool.execute(new MyThread(i)); } // 關閉線程池 pool.shutdown(); } } class MyThread extends Thread { int num; public MyThread(int i){ this.num = i; } @Override public void run() { System.out.println(this.num+" 正在被"+Thread.currentThread().getName() + "執行。。。"); try { Thread.sleep(100L); } catch (InterruptedException e) { e.printStackTrace(); } } }
執行的結果:
0 正在被pool-1-thread-1執行。。。 1 正在被pool-1-thread-2執行。。。 2 正在被pool-1-thread-1執行。。。 3 正在被pool-1-thread-2執行。。。 4 正在被pool-1-thread-1執行。。。 5 正在被pool-1-thread-2執行。。。 6 正在被pool-1-thread-1執行。。。 7 正在被pool-1-thread-2執行。。。 8 正在被pool-1-thread-1執行。。。 9 正在被pool-1-thread-2執行。。。
下面解釋一下程序:
ThreadPoolExecutor 類的方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
參數說明名如下:
corePoolSize - 池中所保存的線程數,包括空閑線程。
maximumPoolSize - 池中允許的最大線程數。
keepAliveTime - 當線程數大於核心時,此為終止前多余的空閑線程等待新任務的最長時間。
unit - keepAliveTime 參數的時間單位。
workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。本例實用的是BlockingQueue,有關它的內容可以參考:http://www.cnblogs.com/liqiu/p/3630281.html
小結:
可見用給定的初始參數和默認的線程工廠及處理程序創建新的 ThreadPoolExecutor。使用 Executors 工廠方法之一比使用此通用構造方法方便得多。