配合源码食用更佳

概述


使用过OkHttp的童鞋们都知道,同步execute(),异步enqueue()
那么如果做到同步异步的呢,其实我们发送的同步/异步请求都会在Dispatcher中管理其状态
其中维护了:
* 运行中的异步请求队列 runningAsyncCalls
* 就绪状态的异步请求队列 readyAsyncCalls
* 运行中的同步请求队列 runningSyncCalls (注意名字的细微差别)
* 线程池 executorService

每当有新的同步请求到Dispatcher碗里来,直接加入到同步运行中队列。
每当有新的异步请求到Dispatcher碗里来,那么通过maxRequests(最大请求数)和maxRequestsPerHost(相同host最大请求数)来判定,是应该进到运行中的队列并立即执行呢,还是进到就绪队列中,等待运行队列有空间了,再进到运行队列中。

同时maxRequestsmaxRequestsPerHost都是可调整的,如果往上调了,运行队列可以进更多请求了,那么就可以将就绪状态的请求移动到运行队列中;如果往下调了,如果运行队列中的请求超过了最大请求数,那也没办法,这些超额请求执行完了,就不能再进那么多了。

下面来看源码。

源码


发送同步/异步请求

/**
发送异步请求
此方法为同步方法,因为runningAsyncCalls和readyAsyncCalls使用的ArrayDeque,然而ArrayDeque是非线程安全的,所以需要同步。
如果运行中的异步请求队列的请求数小于最大请求数且当前请求对应的host下对应的请求数小于maxRequestsPerHost,那么就进队列,并且通过线程池立即执行。
*/
synchronized void enqueue(AsyncCall call) {// 运行队列中的请求数小于maxRequests且相同host的运行中请求数小于maxRequestsPerHost,下面会贴runningCallsForHost()的代码if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {// 加入到运行中队列runningAsyncCalls.add(call);// 使用线程池执行请求,下面会贴出executorService初始化的过程。executorService().execute(call);} else {// 加入就绪队列中readyAsyncCalls.add(call);}
}/**此方法也为同步方法直接加入到运行中同步请求队列中
*/
synchronized void executed(RealCall call) {//加入到同步运行中队列runningSyncCalls.add(call);
}

线程池初始化

/**这是一个同步的懒加载线程池的方法不了解线程池的童鞋请查阅相关资料这里大概的介绍下ThreadPoolExecutor构造器的参数,依顺序来,一个个来* corePoolSize 一直存在于线程池中的线程数量(除非allowCoreThreadTimeOut为true)* maximumPoolSize 线程池中允许存在的最大线程数量* keepAliveTime 除corePoolSize之外的线程,在空闲状态(执行任务完之后)能存在的最大时间* unit 上面那货的单位* workQueue 通过execute方法发送的任务,会先被缓存在这个队列中* threadFactory 创建线程的工厂
*/
public synchronized ExecutorService executorService() {//懒加载if (executorService == null) {//corePoolSize 为 0表示,没有核心线程,所有执行请求的线程,使用完了如果过期了(keepAliveTime)就回收了。//maximumPoolSize 无限大的线程池空间executorService = new ThreadPoolExecutor(0,  //corePoolSizeInteger.MAX_VALUE, //maximumPoolSize60,  //keepAliveTimeTimeUnit.SECONDS,  //unitnew SynchronousQueue<Runnable>(),  //workQueueUtil.threadFactory("OkHttp Dispatcher", false)  //threadFactory);}return executorService;
}

调整请求(就绪/运行)

/**根据maxRequests(最大请求数)和maxRequestsPerHost(相同host最大请求数)来调整runningAsyncCalls(运行中的异步请求)队列和readyAsyncCalls(就绪状态的异步请求)队列。使运行中的异步请求不超过两种最大值,并且如果队列有空闲,将就绪状态的请求归类为运行中。
*/
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();//如果当前请求对应的host下,没有超过maxRequestsPerHost,那么将其从就绪队列中移除,并加入在运行队列。if (runningCallsForHost(call) < maxRequestsPerHost) {//移除i.remove();//加入运行队列runningAsyncCalls.add(call);//立即执行该请求executorService().execute(call);}//如果运行队列已经到达了最大请求数上限,如果还有就绪中的请求,也不管了。if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.}}

获取同个host下的请求数

/**很简单的方法,对比已有的运行中的请求和当前请求的host,相同result++,返回即可
*/
private int runningCallsForHost(AsyncCall call) {int result = 0;for (AsyncCall c : runningAsyncCalls) {if (c.host().equals(call.host())) result++;}return result;
}

请求结束


/**同步请求结束当该同步请求结束的时候,会调用此方法,用于将运行中的同步请求队列中的该请求移除今后系列中,分析RealCall的时候,会知道何时调用此方法的。*/
synchronized void finished(Call call) {if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}/**异步请求结束当该异步请求结束的时候,会调用此方法,用于将运行中的异步请求队列中的该请求移除并调整请求队列,此时就绪队列中的请求可以今后系列中,分析AsyncCall的时候,会知道何时调用此方法的。*/
synchronized void finished(AsyncCall call) {if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");promoteCalls();
}

调整运行队列中的最大请求数量

/**以下两个方法作用类似,前者是调整总的最大请求数,后者是调整单个host下的最大请求数调整之后会有两种情况:1. 当前队列中的请求数大于最大请求数:继续执行,将已在队列中的请求执行完成2. 当前队列中的请求数小于最大请求数:如过就绪队列中存在请求,将其移动到运行队列中,直到运行队列的大小大于等于对大请求数
*/
public synchronized void setMaxRequests(int maxRequests) {// 校验if (maxRequests < 1) {throw new IllegalArgumentException("max < 1: " + maxRequests);}// 设置this.maxRequests = maxRequests;// 调整请求promoteCalls();
}public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {if (maxRequestsPerHost < 1) {throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);}this.maxRequestsPerHost = maxRequestsPerHost;promoteCalls();}

剩余的方法

剩余的方法都是获取一些数据,大家自己看看就好。

总结


Dispatcher的作用为维护请求的状态,并维护一个线程池,用于执行请求。

欢迎交流 QQ:2424334647

One Step By One Step 解析OkHttp3 - Dispatcher (一)相关推荐

  1. 梯度值与参数更新optimizer.zero_grad(),loss.backward、和optimizer.step()、lr_scheduler.step原理解析

    在用pytorch训练模型时,通常会在遍历epochs的过程中依次用到optimizer.zero_grad(),loss.backward.和optimizer.step().lr_schedule ...

  2. 调试的时候step into,step out,step over有什么区别?各有什么作用?分别在什么情况下使用?

    回答1 main() {run();wait();start();} run() {carrun(); } 如果当前箭头在来main(), stepover 就是 只看当前这一层调用函数源名. run ...

  3. Detected call of `lr_scheduler.step()` before `optimizer.step()`.

    在使用pytorch的指数衰减学习率时,出现报错UserWarning: Detected call of `lr_scheduler.step()` before `optimizer.step() ...

  4. pytorch-->optimizer.zero_grad()、loss.backward()、optimizer.step()和scheduler.step()

    优化器optimizer的作用 优化器就是需要根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数值的作用. 一般来说,以下三个函数的使用顺序如下: # compute gradient ...

  5. yolov5 Detected call of `lr_scheduler.step()` before `optimizer.step()`.

    运行yolov5时会提示In PyTorch 1.1.0 and later, you should call them in the opposite order: `optimizer.step( ...

  6. 深入解析OkHttp3

    OkHttp是一个精巧的网络请求库,有如下特性: 1)支持http2,对一台机器的所有请求共享同一个socket 2)内置连接池,支持连接复用,减少延迟 3)支持透明的gzip压缩响应体 4)通过缓存 ...

  7. STEP文件格式(*.step, *.stp)是什么软件。怎样打开

    产品模型数据交换标准STEP是国际标准化组织(ISO)所属技术委员会TC184(工业自动化系统技术委员会)下的"产品模型数据外部表示" (ExternalRepresentatio ...

  8. IDEA调试程序按钮初探 (Step Over/Step Into/Force Step Into/Step Out/Evaluate Expression/Resume Program/条件断点)

    先看界面: 1.可以展开看内部成员变量的值 2.Step Over:执行一行(常用) 3.Step Into:执行一行,如果该行有自定义方法,则运行进入自定义方法 4.Force Step Into: ...

  9. OkHttp 3.x 源码解析之Dispatcher分发器

    Dispatcher概念 Dispatcher中文是分发器的意思,和拦截器不同的是分发器不做Aaction事件处理.只做事件流向.在Okhttp中Dispatcher负责将每一次Requst进行分发, ...

  10. 关于STEP文件格式的介绍

    STEP格式文件是什么? Step文件(也称为STandard for the Exchange of Product data,或简称STEP或ISO 10303)是一种用于表示三维CAD数据的标准 ...

最新文章

  1. Essential Studio for mobile MVC如何创建一个Razor应用程序平台
  2. python怎么读取文件-python怎么读取文件内容
  3. C语言 · 矩阵乘法
  4. JVM调优:G1垃圾回收器
  5. flutter怎么手动刷新_Flutter 小技巧实现通用的局部刷新
  6. ubuntu14.04中离线安装docker
  7. godaddy无法修改域名服务器,godaddy的DNS A记录不能修改原因
  8. 如何进行数据安全管理体系建设?
  9. 【Uplift】模拟数据篇
  10. centos6和centos7的主要区别和版本选择
  11. NVIDIA发布移动超级计算机“Jetson TK1”性能超树莓派
  12. 如何成为一名程序员?
  13. python 数据挖掘_Python数据挖掘框架scikit数据集之iris
  14. python对财务的作用_学习Python对财务工作者有哪些用途?
  15. 三维投影总结:数学原理、投影几何、OpenGL教程、我的方法
  16. tesseract-ocr .NET 识别图片中文字
  17. UE4中蓝图实现输入框输入关键字搜索对应东西
  18. python中的pil模块_初学python的PIL模块
  19. 《程序员练习生》第7期 珍爱生命远离编程
  20. Intelligence math problem,分糖果问题

热门文章

  1. 解决marathon上docker实例一直waitting的问题
  2. 小米10青春版刷鸿蒙,功能齐全也不行!小米10青春版现已跌至新低价,高刷已成趋势?...
  3. 删除的微信好友可以恢复吗?不小心把微信好友删了怎么找回
  4. 【HGE】使用C++从加密压缩包中读取图像文件并显示
  5. appium+python闲鱼采购自动化测试实战
  6. 川大计算机学院李川,川大计算机学院硕导名单_跨考网
  7. 《MultiPoseNet: Fast Multi-Person Pose Estimation using Pose Residual Network》论文阅读
  8. 阿里云国际版建立云端数据库操作流程
  9. c语言编程培训都是小学,小学编程培训班明故宫哪里有C语言培训
  10. 安装运行太极框架Android搞机操作root