配合源碼食用更佳
概述
使用過OkHttp
的童鞋們都知道,同步execute()
咐容,異步enqueue()
舆逃。
那么如果做到同步異步的呢,其實(shí)我們發(fā)送的同步/異步請求都會在Dispatcher
中管理其狀態(tài)戳粒。
其中維護(hù)了:
- 運(yùn)行中的異步請求隊(duì)列 runningAsyncCalls
- 就緒狀態(tài)的異步請求隊(duì)列 readyAsyncCalls
- 運(yùn)行中的同步請求隊(duì)列 runningSyncCalls (注意名字的細(xì)微差別)
- 線程池 executorService
每當(dāng)有新的同步請求到Dispatcher
碗里來路狮,直接加入到同步運(yùn)行中隊(duì)列。
每當(dāng)有新的異步請求到Dispatcher
碗里來享郊,那么通過maxRequests
(最大請求數(shù))和maxRequestsPerHost
(相同host最大請求數(shù))來判定,是應(yīng)該進(jìn)到運(yùn)行中的隊(duì)列并立即執(zhí)行呢孝鹊,還是進(jìn)到就緒隊(duì)列中炊琉,等待運(yùn)行隊(duì)列有空間了,再進(jìn)到運(yùn)行隊(duì)列中又活。
同時maxRequests
和maxRequestsPerHost
都是可調(diào)整的苔咪,如果往上調(diào)了,運(yùn)行隊(duì)列可以進(jìn)更多請求了柳骄,那么就可以將就緒狀態(tài)的請求移動到運(yùn)行隊(duì)列中团赏;如果往下調(diào)了,如果運(yùn)行隊(duì)列中的請求超過了最大請求數(shù)耐薯,那也沒辦法舔清,這些超額請求執(zhí)行完了,就不能再進(jìn)那么多了曲初。
下面來看源碼体谒。
源碼
發(fā)送同步/異步請求
/**
發(fā)送異步請求
此方法為同步方法,因?yàn)閞unningAsyncCalls和readyAsyncCalls使用的ArrayDeque臼婆,然而ArrayDeque是非線程安全的抒痒,所以需要同步。
如果運(yùn)行中的異步請求隊(duì)列的請求數(shù)小于最大請求數(shù)且當(dāng)前請求對應(yīng)的host下對應(yīng)的請求數(shù)小于maxRequestsPerHost颁褂,那么就進(jìn)隊(duì)列故响,并且通過線程池立即執(zhí)行傀广。
*/
synchronized void enqueue(AsyncCall call) {
// 運(yùn)行隊(duì)列中的請求數(shù)小于maxRequests且相同host的運(yùn)行中請求數(shù)小于maxRequestsPerHost,下面會貼runningCallsForHost()的代碼
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
// 加入到運(yùn)行中隊(duì)列
runningAsyncCalls.add(call);
// 使用線程池執(zhí)行請求,下面會貼出executorService初始化的過程彩届。
executorService().execute(call);
} else {
// 加入就緒隊(duì)列中
readyAsyncCalls.add(call);
}
}
/**
此方法也為同步方法
直接加入到運(yùn)行中同步請求隊(duì)列中
*/
synchronized void executed(RealCall call) {
//加入到同步運(yùn)行中隊(duì)列
runningSyncCalls.add(call);
}
線程池初始化
/**
這是一個同步的懶加載線程池的方法
不了解線程池的童鞋請查閱相關(guān)資料
這里大概的介紹下ThreadPoolExecutor構(gòu)造器的參數(shù)伪冰,依順序來,一個個來
* corePoolSize 一直存在于線程池中的線程數(shù)量(除非allowCoreThreadTimeOut為true)
* maximumPoolSize 線程池中允許存在的最大線程數(shù)量
* keepAliveTime 除corePoolSize之外的線程惨缆,在空閑狀態(tài)(執(zhí)行任務(wù)完之后)能存在的最大時間
* unit 上面那貨的單位
* workQueue 通過execute方法發(fā)送的任務(wù)糜值,會先被緩存在這個隊(duì)列中
* threadFactory 創(chuàng)建線程的工廠
*/
public synchronized ExecutorService executorService() {
//懶加載
if (executorService == null) {
//corePoolSize 為 0表示,沒有核心線程坯墨,所有執(zhí)行請求的線程寂汇,使用完了如果過期了(keepAliveTime)就回收了。
//maximumPoolSize 無限大的線程池空間
executorService = new ThreadPoolExecutor(
0, //corePoolSize
Integer.MAX_VALUE, //maximumPoolSize
60, //keepAliveTime
TimeUnit.SECONDS, //unit
new SynchronousQueue<Runnable>(), //workQueue
Util.threadFactory("OkHttp Dispatcher", false) //threadFactory
);
}
return executorService;
}
調(diào)整請求(就緒/運(yùn)行)
/**
根據(jù)maxRequests(最大請求數(shù))和maxRequestsPerHost(相同host最大請求數(shù))來調(diào)整
runningAsyncCalls(運(yùn)行中的異步請求)隊(duì)列和readyAsyncCalls(就緒狀態(tài)的異步請求)隊(duì)列捣染。
使運(yùn)行中的異步請求不超過兩種最大值骄瓣,并且如果隊(duì)列有空閑,將就緒狀態(tài)的請求歸類為運(yùn)行中耍攘。
*/
private void promoteCalls() {
//運(yùn)行中的異步請求隊(duì)列的請求數(shù)大于最大請求數(shù)榕栏,那么就沒必要去將就緒狀態(tài)的請求移動到運(yùn)行中。
//其實(shí)就是說蕾各,如果有超過最大請求數(shù)的請求正在運(yùn)行扒磁,是不需要將其移出隊(duì)列的,繼續(xù)運(yùn)行完即可式曲。
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
//如果就緒的隊(duì)列為空妨托,那就更沒有必要移動了,因?yàn)槎紱]有吝羞。
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
//遍歷就緒隊(duì)列
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
//取出一個請求
AsyncCall call = i.next();
//如果當(dāng)前請求對應(yīng)的host下兰伤,沒有超過maxRequestsPerHost,那么將其從就緒隊(duì)列中移除钧排,并加入在運(yùn)行隊(duì)列敦腔。
if (runningCallsForHost(call) < maxRequestsPerHost) {
//移除
i.remove();
//加入運(yùn)行隊(duì)列
runningAsyncCalls.add(call);
//立即執(zhí)行該請求
executorService().execute(call);
}
//如果運(yùn)行隊(duì)列已經(jīng)到達(dá)了最大請求數(shù)上限,如果還有就緒中的請求恨溜,也不管了符衔。
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
獲取同個host下的請求數(shù)
/**
很簡單的方法,對比已有的運(yùn)行中的請求和當(dāng)前請求的host糟袁,相同result++柏腻,返回即可
*/
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}
請求結(jié)束
/**
同步請求結(jié)束
當(dāng)該同步請求結(jié)束的時候,會調(diào)用此方法系吭,用于將運(yùn)行中的同步請求隊(duì)列中的該請求移除
今后系列中五嫂,分析RealCall的時候,會知道何時調(diào)用此方法的。
*/
synchronized void finished(Call call) {
if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}
/**
異步請求結(jié)束
當(dāng)該異步請求結(jié)束的時候沃缘,會調(diào)用此方法躯枢,用于將運(yùn)行中的異步請求隊(duì)列中的該請求移除并調(diào)整請求隊(duì)列,此時就緒隊(duì)列中的請求可以
今后系列中槐臀,分析AsyncCall的時候锄蹂,會知道何時調(diào)用此方法的。
*/
synchronized void finished(AsyncCall call) {
if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
promoteCalls();
}
調(diào)整運(yùn)行隊(duì)列中的最大請求數(shù)量
/**
以下兩個方法作用類似水慨,前者是調(diào)整總的最大請求數(shù)得糜,后者是調(diào)整單個host下的最大請求數(shù)
調(diào)整之后會有兩種情況:
1. 當(dāng)前隊(duì)列中的請求數(shù)大于最大請求數(shù):繼續(xù)執(zhí)行,將已在隊(duì)列中的請求執(zhí)行完成
2. 當(dāng)前隊(duì)列中的請求數(shù)小于最大請求數(shù):如過就緒隊(duì)列中存在請求晰洒,將其移動到運(yùn)行隊(duì)列中朝抖,直到運(yùn)行隊(duì)列的大小大于等于對大請求數(shù)
*/
public synchronized void setMaxRequests(int maxRequests) {
// 校驗(yàn)
if (maxRequests < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequests);
}
// 設(shè)置
this.maxRequests = maxRequests;
// 調(diào)整請求
promoteCalls();
}
public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
if (maxRequestsPerHost < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
}
this.maxRequestsPerHost = maxRequestsPerHost;
promoteCalls();
}
剩余的方法
剩余的方法都是獲取一些數(shù)據(jù),大家自己看看就好谍珊。
總結(jié)
Dispatcher
的作用為維護(hù)請求的狀態(tài)治宣,并維護(hù)一個線程池,用于執(zhí)行請求砌滞。
歡迎交流 QQ:2424334647