As everyone knows,線程池在java裏就佔有一定的分量,而這一點在Android上也有很廣泛的應用。曾經無數次被問到過,什麼是線程池,作用是什麼?和線程的關係。是不是感覺頭都大了,今天我就來整理一份關於線程池的一些內容。申明:此爲個人學習總結part
1.什麼是線程池?
理論上來說,線程池其實就是一個管理線程的地方。
2.爲什麼會有線程池這個東西的存在?
在以往的開發中我們需要子線程操作的時候,我們就隨時隨地的new一個Thread,或者弄一個Runnable來開啓一個子線程,如果數量多了,以致於到處都是new的子線程,很多子線程空閒在那裏,有些完成後又被GC,不停的GC,新建和閒置,造成浪費資源,還有可能會造成界面卡頓,對於線程的控制也很混亂。這個時候我們就可以用線程池,用線程池對項目的子線程進行管理和控制。
3.線程池的好處?
(a)線程的複用,重用已經創建好的線程,避免重複的創建和頻繁的GC。
(b) 控制線程併發數,合理使用系統資源。
(c)可以有效控制線程的執行。定時執行,取消執行等操作。
探索線程池的用法
我們大概知道了線程池的作用和優點,接下來讓我們瞭解一下它的一些類,一些方法,一些參數的意思。
1.ThreadPoolExecutor:它的構造方法有4種。這裏挑最多參數的那組來說一下,因爲最多的已經包含了其他的構造器裏的參數。參數如下:
<a>corePoolSize 線程池中核心線程的數量
<b>maximumPoolSize 線程池中最大線程數量
<c>keepAliveTime 非核心線程的超時時長。在超過這個時間之後,非核心線程會被回收。若想在超時後回收核心線程,就設置ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true,則這個參數也表示這個核心線程的超時時長。
<c>unit 第三個參數的單位
<d>workQueue 線程池中的任務隊列(和Handler的MessageQueue有點相似),這裏面存儲的都是那些通過ThreadPoolExecutor的execute提交的任務,這些任務是已經提交但是還未執行的那些任務。
<e>threadFactory 爲線程池提供創建新線程的功能。
<f>handler
這個workQueue是一個BlockingQueue類型(阻塞隊列).
而BlockingQueue又有如下類型:
(1)ArrayBlockingQueue:表示規定大小的BlockingQueue,存儲在此的數據是先進先出(FIFO原則)
(2)LinkedBlockingQueue:表示大小不確定的BlockingQueue,LinkedBlockingQueue的構造函數裏面有一個int值,給這個int一個值,那麼代表這個LinkedBlockingQueue是有大小的。如果不給值,默認則是Interger.MAX_VALUE.
(3)priorityBlockingQueue:和LinkedBlockingQueue類似,但是它不是FIFO原則。它是由Comparator來決定存取順序的(比如說按照年齡,身高排序)
(4)synchronousQueue:它是線程安全的BlockingQueue,它內部沒有數據緩存空間。它的理解較爲抽象,我們不能遍歷讀取其中的數據,而是存入和取出的時候會走SynchronousQueue過一下,所以說,在取走的時候這個數據此時纔在隊列裏面存在一下。
你一定還想知道線程放到線程池之後是怎麼去運行的呢?
前提都是在execute一個線程之後.......
(1)線程池的線程數<核心線程數,會立刻啓用一個核心線程去執行
(2)線程池的線程數=核心線程數,而workQueue未滿,則將新線程放入workQueue中等待執行。
(3)線程池的線程數=核心線程數,但是<非核心線程數,而workQueue已滿,則開啓一個非核心線程來執行任務。
(4)線程池的線程數>非核心線程數,則拒絕執行該任務。
接下來介紹幾種線程池的類型
FixedThreadPool:核心線程數固定的線程池。
private void mFixedThreadPool(){
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i=0;i<30;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000);
}
};
executorService.execute(runnable);
}
}
結果是:首先就在覈心線程添加3個任務0,1,2的任務。然後再添加任務在workQueue,若核心線程有空了,就會來處理workQueue任務隊列的任務。
singleThreadPool:核心線程數只有1條。但使用它可以避免去處理線程同步問題。
private void mSingleThreadPool(){
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i=0;i<30;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000);
}
};
executorService.execute(runnable);
}
}
結果是:首先就核心線程添加1個0的任務。然後再添加任務在workQueue裏....
CachedThreadPool:根據程序運行自動調整線程池中的線程數量。最大爲Interger.MAX_VALUE.所以它適合大量任務請求的時候。CachedThreadPool中沒有新任務的時候,裏面的線程會因爲超時而被終止。
private void mCachedThreadPool(){
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0;i<30;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
executorService.execute(runnable);
SystemClock.sleep(3000);//每隔3s在添加新任務。以致於之前的線程又空閒了,所以執行任務的應該都是那一個線程。
}
}
ScheduledThreadPool:具有定時定期執行任務功能的線程池。核心線程數是固定的,但非核心線程數是無窮大的,所以在非核心線程閒置的時候會立即被回收。
(1)延遲啓動任務
//延遲啓動任務
private void mDelayScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
executorService.schedule(runnable,2, TimeUnit.SECONDS);
}
(2)延遲定時執行任務
//延遲定時執行任務
private void mTimeScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
executorService.scheduleAtFixedRate(runnable,1,1,TimeUnit.SECONDS);
}
延時1s後,每隔1s執行一個新任務。
(3)延遲執行任務
//延遲執行任務
private void mTime2ScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
//第一次延遲1s,以後每次也延遲一秒執行
executorService.scheduleWithFixedDelay(runnable,1,1,TimeUnit.SECONDS);
}
在線程池裏面還有很多其他的比較常用的方法。
submit:一般情況我們用execute來提交任務,但是有的時候我們還會用到submit,因爲submit有返回值。
如下所示,callable接口實現異步任務,在call方法中來執行異步任務,那麼突然要問一句,返回值是什麼呢?你一定會有的懵逼,醬醬醬~其實返回值就是該任務的返回值。
你是不是要問我Future是什麼?哎呀,其實Future就是返回結果,返回他的isDone屬性表示異步任務執行成功。
private void mSubmint(){
List<Future<String>> futures=new ArrayList<>();
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
for(int i=0;i<10;i++){
Future<String> taskFuture=threadPoolExecutor.submit(new MyTask(i));
futures.add(taskFuture);
}
//遍歷所有任務的結果
for(Future<String> future:futures){
try {
Log.i("mylog","future="+future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyTask implements Callable<String>{
private int taskId;
public MyTask(int taskId) {
this.taskId = taskId;
}
@Override
public String call() throws Exception {
SystemClock.sleep(1000);
//返回每一個任務的執行結果
return "TaskName="+Thread.currentThread().getName()+"//TaskId="+taskId;
}
}
除了使用submit可以讓執行的任務有返回值之外,還有一種方式。那就是-----自定義線程池。
自定義線程池,那麼那就是自定義ThreadPoolExecutor.
//自定義線程池
private void customThreadPool(View view){
MyThreadPool myThreadPool=new MyThreadPool(3,5,2000,TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
for(int i=0;i<10;i++){
final int finalI=i;
Runnable runnable=new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.i("myLog","finalId="+finalI);
}
};
myThreadPool.execute(runnable);
}
}
class MyThreadPool extends ThreadPoolExecutor{
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
Log.i("myLog","開始執行任務");
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Log.i("myLog","任務執行結束");
}
@Override
protected void terminated() {
super.terminated();
Log.i("myLog","線程池關閉");
}
}