1.線程池的引入
引入的好處
1)提升性能(創建和消耗對象費時費CPU資源);2)防止內存過度消耗(控制活動線程的數量,防止併發線程過多);使用條件
在Android中當同時併發多個網絡線程時,引入線程池技術會極大地提高APP的性能。
例如:多線程下載,點一個下載一個(假設允許最多同時下載五個),當點到第六個的時候開始等待,這就涉及到線程的管理
2.封裝線程池管理者
1)jdk自身帶有線程池的實現類ThreadPoolExecutor
2)ThreadPoolManager是基於ThreadPoolExecutor進行了封裝的類
其中,獲取當前可用的處理器核心數我們用Runtime.getRuntime().availableProcessors(),我們來看它的註釋:
-
package com.example.threadpooldemo;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 線程池管理
* 管理整個項目中所有的線程,所以不能有多個實例對象
*/
public class ThreadPoolManager {
/**
* 單例設計模式(餓漢式)
* 單例首先私有化構造方法,然後餓漢式一開始就開始創建,並提供get方法
*/
private static ThreadPoolManager mInstance = new ThreadPoolManager();
public static ThreadPoolManager getInstance() {
return mInstance;
}
private int corePoolSize;//核心線程池的數量,同時能夠執行的線程數量
private int maximumPoolSize;//最大線程池數量,表示當緩衝隊列滿的時候能繼續容納的等待任務的數量
private long keepAliveTime = 1;//存活時間
private TimeUnit unit = TimeUnit.HOURS;
private ThreadPoolExecutor executor;
private ThreadPoolManager() {
/**
* 給corePoolSize賦值:當前設備可用處理器核心數*2 + 1,能夠讓cpu的效率得到最大程度執行(有研究論證的)
*/
corePoolSize = Runtime.getRuntime().availableProcessors()*2+1;
maximumPoolSize = corePoolSize;//雖然maximumPoolSize用不到,但是需要賦值,否則報錯
executor = new ThreadPoolExecutor(
corePoolSize, //當某個核心任務執行完畢,會依次從緩衝隊列中取出等待任務
maximumPoolSize, //5,先corePoolSize,然後new LinkedBlockingQueue<Runnable>(),然後maximumPoolSize,但是它的數量是包含了corePoolSize的
keepAliveTime,//表示的是maximumPoolSize當中等待任務的存活時間
unit,
new LinkedBlockingQueue<Runnable>(),//緩衝隊列,用於存放等待任務,Linked的先進先出
Executors.defaultThreadFactory(),//創建線程的工廠
new ThreadPoolExecutor.AbortPolicy()//用來對超出maximumPoolSize的任務的處理策略
);
}
/**
* 執行任務
*/
public void execute(Runnable runnable){
if(runnable==null)return;
executor.execute(runnable);
}
/**
* 從線程池中移除任務
*/
public void remove(Runnable runnable){
if(runnable==null)return;
executor.remove(runnable);
}
} -
1>ThreadPoolExecutor說明文檔:
ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
corePoolSize: 核心線程數,能夠同時執行的任務數量;
maximumPoolSize:除去緩衝隊列中等待的任務,最大能容納的任務數(其實是包括了核心線程池數量);
keepAliveTime:超出workQueue的等待任務的存活時間,就是指maximumPoolSize裏面的等待任務的存活時間;
unit:時間單位;
workQueue:阻塞等待線程的隊列,一般使用new LinkedBlockingQueue<Runnable>()這個,如果不指定容量,會一直往裏邊添加,沒有限制,workQueue永遠不會滿;
threadFactory:創建線程的工廠,使用系統默認的類;
handler:當任務數超過maximumPoolSize時,對任務的處理策略,默認策略是拒絕添加;
2>執行流程:
當線程數小於corePoolSize時,每添加一個任務,則立即開啓線程執行;當corePoolSize滿的時候,後面添加的任務將放入緩衝隊列workQueue等待;當workQueue也滿的時候,看是否超過maximumPoolSize線程數,如果超過,默認拒絕執行。
3>舉例說明:
假如corePoolSize=2,maximumPoolSize=3,workQueue容量爲8;最開始,執行的任務A,B,此時corePoolSize已用完,再次執行任務C,則C將被放入緩衝隊列workQueue中等待着,如果後來又添加了7個任務,此時workQueue已滿,則後面再來的任務將會和maximumPoolSize比較,由於maximumPoolSize爲3,所以只能容納1個了,因爲有2個在corePoolSize中運行了,所以後面來的任務默認都會被拒絕。
3)Runnable與線程的區別Runnable只是一個接口,它的源碼如下,而線程是真正開啓系統資源去執行任務,他們兩個,線程是真正消耗系統資源的
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
package java.lang;
-
-
-
-
-
-
-
public interface Runnable {
-
-
-
-
-
-
-
public void run();
-
}
-
3.線程池例子
下面是一個線程池的例子(演示多線程執行任務),以加深對原理的理解
1)引入我們封裝好的ThreadPoolManager.Java
2)演示功能
-
package com.example.threadpooldemo;
import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
/**
* 演示線程池
*
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 創建九個任務
*/
for (int i = 0; i < 9; i++) {
ThreadPoolManager.getInstance().execute(new DownloadTask(i));
}
}
/**
* 模仿下載任務,實現Runnable
*/
class DownloadTask implements Runnable{
private int num;
public DownloadTask(int num) {
super();
this.num = num;
Log.d("JAVA", "task - "+num + " 等待中...");
}
@Override
public void run() {
Log.d("JAVA", "task - "+num + " 開始執行了...開始執行了...");
SystemClock.sleep(5000); //模擬延時執行的時間
Log.e("JAVA", "task - "+num + " 結束了...");
}
}
} -
打印結果如下:
項目源碼,點擊下載