深入OKHttp源码分析(二)----OkHttp任务调度核心类Dispatcher解析
OkHttp任務調度核心類Dispatcher解析
上一篇我們分析了okhttp的同步和異步請求的執行流程并進行了源碼分析,深入OKHttp源碼分析(一)----同步和異步請求流程和源碼分析 那么今天我們來看看在整個執行流程中起到關鍵作用的Dispatcher調度類。首先我們來看看這個類中的幾個全局變量
/** Executes calls. Created lazily. */private @Nullable ExecutorService executorService;/** Ready async calls in the order they'll be run. */private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); 復制代碼我們先來看下面三個變量 readyAsyncCalls:等待執行的異步任務隊列,用來存放異步任務,如果線程池中的任務數量已經超過或已經達到指定的最大的任務數,那么久將異步任務放入這個隊列中。 runningAsyncCalls:正在執行的異步任務隊列,如果線程池中的任務數量小于指定的最大任務數,就將異步任務添加到這個隊列中, runningSyncCalls:正在執行的同步任務隊列
接著回來我們看executorService,這個是一個線程池,怎么證明呢,我們可以看到下面這個方法
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出來一個線程池并復制給了executorService,我們來看下線程池的構造函數中的參數,來一一分析下: 第一個參數0,指的是這個線程池中的核心線程數量,如果線程池中沒有任務的話,那么非核心線程在經過一段時間的等待后就會關閉,以節省資源。 第二個參數Integer.MAX_VALUE,這個參數指的是這個線程池中的最大線程數,設置成了integer的最大值,我們知道,這個值是非常大的,如果真的創建了這么多的線程,那么我們的手機不會掛掉嗎???其實不會的,因為okhttp在其他地方已經進行了判斷,如果線程池中的線程數量達到了指定的數值,就不會將任務添加到線程池中,也就不會新建線程了。具體是在哪里判斷的,我們稍后介紹。 第三個參數60和第四個參數TimeUnit.SECONDS,這個指的就是空閑線程的存活時間,第三個參數指定數值,第四個參數指定單位。 第五個參數new SynchronousQueue():其實就是線程池中的線程的管理隊列 第六個參數Util.threadFactory("OkHttp Dispatcher", false):這個是線程的創建工廠,線程的創建就是通過第六個參數進行的,有興趣的朋友可點進去繼續看下,我們就不貼出來啦。
我們再來看下上一篇遇到過的一個方法
synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);executorService().execute(call);} else {readyAsyncCalls.add(call);}} 復制代碼我們看下if語句的條件中的兩個變量maxRequests,maxRequestsPerHost,我們看下定義,
private int maxRequests = 64;private int maxRequestsPerHost = 5; 復制代碼這兩個值是什么意思呢,其實,maxRequests就是線程池中的最大任務數,當線程池中的任務數量達到這個值之后,就不再往線程池中繼續添加任務,所以,即使在創建線程池的時候指定了線程池的最大任務數是integer的最大值,也是不可能達到的,這就是原因。maxRequestsPerHost是每個主機的最大請求數,當線程池中的任務對同一個主機的請求達到這個數值之后,就不能繼續往線程池中添加對這個主機的請求任務,不知道這樣說大家能不能理解???? 我們回過頭來看下這條語句runningCallsForHost(call),點進去看下這個方法的實現
private int runningCallsForHost(AsyncCall call) {int result = 0;for (AsyncCall c : runningAsyncCalls) {if (c.get().forWebSocket) continue;if (c.host().equals(call.host())) result++;}return result;} 復制代碼我們可以看到,里面是對線程池中正在執行的任務進行遍歷操作,當要添加的任務的請求的主機和正在進行的任務的主機一致,result就加1,最后得到對這個主機的請求數量,然后在前面的if語句中進行判斷是否小于指定的值,如果小于,就將異步任務添加到正在執行的異步任務隊列中,并添加到線程池中,否則就添加到等待的異步任務隊列中。
我們再來看一個前面分析過的方法,finished方法,這個方法在哪里會被調用呢?如果你不清楚,請看前面一篇文章的分析,我們看下這個方法的實現
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {int runningCallsCount;Runnable idleCallback;synchronized (this) {if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");if (promoteCalls) promoteCalls();runningCallsCount = runningCallsCount();idleCallback = this.idleCallback;}if (runningCallsCount == 0 && idleCallback != null) {idleCallback.run();}} 復制代碼在加鎖的代碼塊中,首先將這個異步請求的任務從執行的隊列中移除,然后我們看下這句
if (promoteCalls) promoteCalls(); 復制代碼我們看下實現
private void promoteCalls() {if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {AsyncCall call = i.next();if (runningCallsForHost(call) < maxRequestsPerHost) {i.remove();runningAsyncCalls.add(call);executorService().execute(call);}if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.}} 復制代碼首先判斷當前正在運行的隊列中的任務數量是否達到了指定的數值,如果等于或大于指定數值,就返回,接著判斷等待隊列是否為空,如果為空,說明沒有要添加的任務,也返回即可。 下面對等待的任務隊列進行遍歷,首先取出一個異步任務,判斷該任務請求的主機地址在正在執行的隊列中的數量是否達到了指定的最大任務數,如果沒有達到,就從等待隊列中移除,并添加到正在運行的隊列中,并加入線程池中執行。 最后判斷正在運行的任務隊列中的任務數量是否達到了指定的最大任務數,如果大于最大任務數,就跳出for循環,否則,就繼續從等待隊列中取出任務進行操作。 這個方法分析完了,我們回到finished方法中,繼續往下看
runningCallsCount = runningCallsCount(); 復制代碼上一篇我們介紹過,這個語句只是重新計算了正在執行的任務數量,包括同步和異步任務。 finished方法最后部分是判斷運行任務數量是否為0,如果沒有了運行任務,就執行回收線程進行資源回收操作。 到此為止呢,我們就將okhttp的任務調度核心類Dispatcher的主要代碼又分析了一遍,相信大家能對這個類有了較為深刻的認識了,這個類的主要功能就是對同步和異步任務進行調度處理,相當于任務的指揮者,大概就像是十字路口的交警同志吧,讓你等你就等,讓你走你就得走,可能比喻不是非常恰當,只要大家都明白即可,下一篇我們就對okhttp的攔截器進行介紹和分析。
總結
以上是生活随笔為你收集整理的深入OKHttp源码分析(二)----OkHttp任务调度核心类Dispatcher解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2018-04-12
- 下一篇: 你和阿里资深架构师之间,差的不仅仅是年龄