多線程的軟件設計方法確實可以最大限度的發揮現代多核心處理器的計算能力,提高生產系統的吞吐量和性能,但是若不加控制和管理的隨意使用線程,對熊的性能反而產生了不力的影響.
在實際生產環境中,線程的數量必須得到控制,盲目的大量創建線程對系統性能是有傷害的.
- 什么是線程池:
為了避免系統頻繁的創建和銷毀線程,我們可以讓創建的線程進行復用,大家對數據庫連接池肯定不陌生,線程池也是一個目的,線程池中,總有那么幾條活躍的線程,當你需要線程的時候,可以從池子中隨便哪一個空閑線程,當完成工作時,並不着急關閉線程,而是將這個線程退回到池子,方便其他人使用.
簡而言之,在使用線程池后,創建線程變成了從線程池獲得空閑線程,關閉線程變成了歸還線程,
- 不重復造輪子:JDK對線程池的支持
JDK提供了一套Executor框架:


以上成員均在Java.util.cocurrent包中,是JDK的核心類,其中ThreadPoolExecutor表示一個線程池,Executors類則扮演者線程池工廠的角色,通過Executors可以取得一個擁有特定功能的線程池,
Executor框架提供了各種類型的線程池,主要有以下工廠方法:
public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newSingleThreadExecutor() public static ExecutorService newCachedThreadPool() public static ScheduledExecutorService newSingleThreadScheduledExecutor() public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
- newFixedThreadPool()方法. 該方法返回一個固定線程數量的線程池,該線程池中的線程數量始終不變,當有一個新任務時,線程池中若有空閑線程,則立即執行,若沒有,則新任務會被暫時存在一個隊列中,得有空閑線程時,便處理在任務隊列中的任務
- newSingleThreadExecutor()方法,改方法返回一個只有一個線程的線程池,若多余一個任務唄提交到該線程池,任務會被保存在一個隊伍隊列,帶線程空閑,按先入先出的順序執行隊列中的任務,
- newCachedThreadPool()方法,該方法返回一個可根據實際情況調整線程數量的線程池.線程池數量是不確定的,但若有空閑線程可以復用,則會優先使用可以復用的線程,若所有線程均在工作,又有新的任務提交,則會創建新額線程處理任務,所有線程在當前任務執行完畢后,將返回線程池進行復用,
- newSingleThreadScheduledExecutor()方法: 改方法返回一個ScheduledExecutorService對象,線程池大小為1 這個接口在ExecutorService接口之上拓展了在給定時間執行某任務的功能,如在某個固定的延時之后執行,或者周期性執行某個任務.
- newScheduledThreadPool()方法:改方法也返回一個ScheduledExecutorService對象 但改線程池可以指定線程數量
- 固定大小的線程池.
- 計划任務
另一個值得注意的方法是newScheduledThreadPool().它返回一個ScheduledExecutorService對象,可以根據時間需要對線程進行調度.他的一些主要方法如下:
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
於其他幾個線程池不同,ScheduledExecutorService並不一定會立即安排執行任務,他其實是起到了計划任務的作用,他會在指定的時間,對任務進行調度.
方法schedule()會在給定時間,對任務進行一次調度,方法scheduleAtFixedRate()和scheduleWithFixedDelay()會對任務進行周期性的調度,但兩者有小小的區別
- scheduleAtFixedRate 創建一個周期性的任務,任務開始於給定的初始延遲時間,后續任務按照給定的周期驚喜,后續一個任務將會在initialDelay+period時候執行,后續第二個任務將在initialDetay+2*period時進行,
- scheduleWithFixedDelay 創建並執行一個周期性任務,任務開始於給定的初始延遲時間,后續任務將會按照給定的延時進行,即上一個任務的結束時間到下一個任務的開始時間的時間差,
我們寫具體的demo來試驗:
public class ScheduledExecutorServiceDemo { public static void main(String[] args) { ScheduledExecutorService ses = Executors.newScheduledThreadPool(10); ses.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { Thread.sleep(1000); System.out.println(System.currentTimeMillis() / 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }, 0, 2, TimeUnit.SECONDS); } }
執行結果


0代表立即執行 然后每個2秒執行一次. 時間間隔是2秒 如果我們把線程的休眠時間設置為8s


我們發現時間間隔變成了8s
public class ScheduledExecutorServiceDemo { public static void main(String[] args) { ScheduledExecutorService ses = Executors.newScheduledThreadPool(10); /* ses.scheduleAtFixedRate(() -> { try { Thread.sleep(1000); System.out.println(System.currentTimeMillis() / 1000); } catch (InterruptedException e) { e.printStackTrace(); } }, 0, 2, TimeUnit.SECONDS);*/ ses.scheduleWithFixedDelay(() -> { try { Thread.sleep(1000); System.out.println(System.currentTimeMillis() / 1000); } catch (InterruptedException e) { e.printStackTrace(); } }, 1, 2, TimeUnit.SECONDS); } }
執行結果


我們發現時間間隔是3s 休眠的1s+2 當我們把休眠時間改成8s 時間間隔就是10s


我們發現 scheduleAtFixedRate這個方法是 線程執行完立即執行下一次,所以當執行時間大於period時 周期就是線程的執行時間 而scheduleWithFixedDelay方法是線程執行完成后等待delay時間后在執行,所以周期是執行時間+delay