您的位置:首页 > 理论基础 > 计算机网络

Okhttp任务队列工作原理

2016-08-24 16:23 411 查看
应用层:

1.讲请求添加到分发起中 Client.dispatcher().executed(this);

2.创建截获器ApplicationInterceptorChain(可对请求进行压缩,修改编码等处理)

逻辑层

1.数据缓存:

1)缓存的数据没有过期,直接使用

2)缓存的数据过期,先用过期的,再去网络请求数据,并更新本地数据

3)缓存的数据没有,去网络直接获取

2.底层使用连接池

3.路线选择器(路由选择,选择最优)

4.网络截获器可以有多个(依据目标地址是否有重定向来觉得)

链路层

1.底层是Socket

1 概述

1.1 线程池ThreadPoolExecutor

ThreadPoolExecutor是java线程创建工具。存在于java.util.concurrent 包中。

看构造方法:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)


corePoolSize : 最小并发线程数。

maximumPoolSize :线程池中最大的线程池并发数。

keepAliveTime : 当线程的数目大于

corePoolSize时,线程的最大存活时间。

unit : 时间单位 BlockingQueue 工作队列

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;
}


1.2 Call对象

okttp的操作元是Call对象。异步的实现是RealCall.AsyncCall。而 AsyncCall是实现的一个Runnable接口。所以Call本质就是一个Runable线程操作元肯定是放进excutorService中直接启动的。

final class AsyncCall extends NamedRunnable {}


private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();


2.2 过程分析

Call代用enqueue方法的时候

synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}


方法中满足执行队列里面不足最大线程数maxRequests并且Call对应的host数目不超过maxRequestsPerHost 的时候直接把call对象直接推入到执行队列里,并启动线程任务(Call本质是一个Runnable)。否则,当前线程数过多,就把他推入到等待队列中。Call执行完肯定需要在runningAsyncCalls 队列中移除这个线程。那么readyAsyncCalls队列中的线程在什么时候才会被执行呢。

追溯下AsyncCall 线程的执行方法

@Override
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket);
if (canceled) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}


这里做了核心request的动作,并把失败和回复数据的结果通过responseCallback 回调到Dispatcher。执行操作完毕了之后不管有无异常都会进入到dispactcher的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();
}
}


在这里call在runningAsyncCalls队列中被移除了,重新计算了目前正在执行的线程数量。并且调用了promoteCalls() 看来是来调整任务队列的

private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests)
return; //
if (readyAsyncCalls.isEmpty()) return;
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;
}
}


原来实在这里对readyAsyncCalls 进行调度的。最终会在readyAsyncCalls 中通过remove操作把最后一个元素取出并移除之后加入到runningAsyncCalls的执行队列中执行操作。ArrayDeque 是非线程安全的所以finished在调用promoteCalls 的时候都在synchronized块中执行的。执行等待队列线程当然的前提是runningAsyncCalls 线程数没有超上线,而且等待队列里面有等待的任务。

小结,Call在执行任务通过Dispatcher把单元任务优先推到执行队列里进行操作,如果操作完成再执行等待队列的任务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程 线程池