1、在什麼情況下使用線程池?
1.單個任務處理的時間比較短
2.將需處理的任務的數量大
2、使用線程池的好處
1.減少在創建和銷燬線程上所花的時間以及系統資源的開銷
2.如不使用線程池,有可能造成系統創建大量線程而導致消耗完系統內存以及”過度切換”。
3、JDK自帶線程池總類介紹
1、newFixedThreadPool創建一個指定工作線程數量的線程池。每當提交一個任務就創建一個工作線程,如果工作線程數量達到線程池初始的最大數,則將提交的任務存入到池隊列中。
2、newCachedThreadPool創建一個可緩存的線程池。這種類型的線程池特點是:
1).工作線程的創建數量幾乎沒有限制(其實也有限制的,數目爲Interger. MAX_VALUE), 這樣可靈活的往線程池中添加線程。
2).如果長時間沒有往線程池中提交任務,即如果工作線程空閒了指定的時間(默認爲1分鐘),則該工作線程將自動終止。終止後,如果你又提交了新的任務,則線程池重新創建一個工作線程。
3、newSingleThreadExecutor創建一個單線程化的Executor,即只創建唯一的工作者線程來執行任務,如果這個線程異常結束,會有另一個取代它,保證順序執行(我覺得這點是它的特色)。單工作線程最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個線程是活動的 。
4、newScheduleThreadPool創建一個定長的線程池,而且支持定時的以及週期性的任務執行,類似於Timer。(這種線程池原理暫還沒完全瞭解透徹)
總結: 一.FixedThreadPool是一個典型且優秀的線程池,它具有線程池提高程序效率和節省創建線程時所耗的開銷的優點。但是,在線程池 空閒時,即線程池中沒有可運行任務時,它不會釋放工作線程,還會佔用一定的系統資源。
二.CachedThreadPool的特點就是在線程池空閒時,即線程池中沒有可運行任務時,它會釋放工作線程,從而釋放工作線程所佔用的資源。但是,但當出現新任務時,又要創建一新的工作線程,又要一定的系統開銷。並且,在使用CachedThreadPool時,一定要注意控制任務的數量,否則,由於大量線程同時運行,很有會造成系統癱瘓。
4、代碼詳解
package com.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* java 線程池學習
*/
public class ThreadPoolTest {
/**
* 線程池的概念
* jdk5提出了ThreadPool的概念
* 之所以採用線程池的主要原因在於:
* 線程時間=T1(創建時間)+T2(運行時間)+T3(銷燬時間)
* 線程池則可以一個線程空閒下來後爲另一個服務,這樣子,就達到了整體的效果,
* 嘿嘿(敏捷團隊的每一個人一樣)
*
* 四種種靜態工廠線程池
*
* 固定線程池
* 緩存線程池
* 單一線程池 (採用替補的方式)
* 另外,還有定時器的線程池 (使用可以參考Timer的使用)
*/
public static void main(String[] args) {
/**
* 測試實例說明
* 有100個任務,一次提交給線程池10個,分析線程池,每次的處理情況
*/
//固定的線程池
// ExecutorService threadPool = Executors.newFixedThreadPool(3); //Executor是一個工具類,聲明瞭一個具有三個線程的線程池
//緩存的線程池 ,根據任務量自動增加線程數量,和回收 ,就會自動增加到十個線程
// ExecutorService threadPool = Executors.newCachedThreadPool();
//線程池中只有一個線程,但是線程死了後可以對另一個線程調用重新啓動,(單線程池)
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0;i<10;i++){
final int finalI = i;
threadPool.execute(new Runnable() { //任務執行
@Override
public void run() {
for(int j = 0;j<10;j++){
try {
Thread.sleep(20); //便於輸出觀看
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" loop of " + finalI +
" for task of "+ j); //某個線程正在進行第幾次循環
}
}
});
}
System.out.println("all of 10 tasks have committed");
// threadPool.shutdown();
//採用定時任務,定時器任務類型
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("bombing");
}
},5,2, TimeUnit.SECONDS); //5秒之後炸,每個2秒炸一下 ,schedule(定時任務)或者schedule之後定時,最後一個是單位
}
}
/**分析
* 真正的線程接口是ExecutorService,而Executor是一個頂級的接口。
* 線程池的編程模式下,任務是提交給整個池子,由池子來負責分配任務;相當於,先接手(接受任務防止長時間的等待),接手後進行細節分配。
* 任務是提交給線程池的,但是一個線程只能執行一個任務,但是可以同時向線程池提交多個任務。
*
* 但是對於高端的使用,需要自己定製線程池
*/
比較:
ExecutorService |
真正的線程池接口。 |
ScheduledExecutorService |
能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。 |
ThreadPoolExecutor |
ExecutorService的默認實現。 |
ScheduledThreadPoolExecutor |
繼承ThreadPoolExecutor的ScheduledExecutorService接口實現,週期性任務調度的類實現。 |
5、用途及用法
網絡請求通常有兩種形式:第一種,請求不是很頻繁,而且每次連接後會保持相當一段時間來讀數據或者寫數據,最後斷開,如文件下載,網絡流媒體等。另一種形式是請求頻繁,但是連接上以後讀/寫很少量的數據就斷開連接。考慮到服務的併發問題,如果每個請求來到以後服務都爲它啓動一個線程,那麼這對服務的資源可能會造成很大的浪費