參考:http://www.lxweimin.com/p/6637369d02e7
1. 線程池基礎
1.1. 線程池好處
初學Android時異步都用new Thread+Handler,但是java還有個利器,線程池。比如Picasso、Rxjava等框架使用的都是線程池。
線程池的關鍵在于線程復用以減少非核心任務的損耗。主要原因在于減少了創建線程、銷毀線程的時間。
1.2. OkHttp中的線程池
首先,OkHttp擁有2種運行方式,一種是同步阻塞調用并直接返回的形式,另一種是通過內部線程池分發調度實現非阻塞的異步回調。這兩講的是第二種方式。
OkHttp配置的的線程池在Dispatcher類里:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
new了一個核心線程(或最小保留線程)為0、最大線程數為Integer.MAX_VALUE、空閑線程存活時間為60s的線程池。但是Dispatcher默認同時支持最多64個并發請求,單個IP最多5個請求(可以設定)。
1.3. OkHttp任務調度
在OkHttp的異步請求時,一般進行如下構造
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder()
.url("http://qq.com").get().build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) { }
@Override
public void onResponse(Call call, Response response) throws IOException { }
}
);
但是最終enqueue執行的是Dispatcher的入隊操作:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加正在運行的請求
runningAsyncCalls.add(call);
//線程池執行請求
executorService().execute(call);
} else {
//添加到緩存隊列排隊等待
readyAsyncCalls.add(call);
}
}
這里的maxRequests 就是默認最大并發64,maxRequestsPerHost則為每個ip最大并發數。
1.4. AsyncCall
從上面的代碼可以看到Dispatcher的enqueue方法中需要的是AsyncCall。參考原文可以知道finally中執行了這行代碼:
client.dispatcher().finished(this);
這行代碼有兩個作用,1、完成本次執行的任務。2、調用下個任務的執行。這是OkHttp一個很出彩的地方,就是在try/finally中調用了finished函數,可以主動控制等待隊列的移動,而不是采用鎖,極大減少了編碼復雜性。
------總結-----
1、在多線程任務調度中可以用類似反向代理(如似于Nginx)與線程池配合實現了高并發,低阻塞的運行
2、在AsyncCall 中采用try/finally中調用了finished函數,主動控制等待隊列的移動,而不是采用鎖
3、采用Deque作為緩存,按照入隊的順序先進先出