源码位于Dispatcher.java(依赖用的是3.8.1版本,Kotlin工作量有点大。。。)
/** Ready async calls in the order they’ll be run. */
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven’t finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven’t finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
调度器 dispatcher 管理了三个队列:同步运行队列,异步运行队列、异步等待队列。同步暂时不做分析(加载耗时太长,用不到)异步如图所示,注Call本质是一个Runnable。
Runnable与Thread的区别
Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。
异步请求最大同时请求数量
private int maxRequests = 64;
异步请求同一域名同时存在的最大请求数量
private int maxRequestsPerHost = 5;
执行过程
- 用户调用最后一步为enqueue,
- 执行队列里面不足最大线程数maxRequests 并且Call 对应的host 数目不超过maxRequestsPerHost 的时候直接把call 对象直接推入到执行队列
- 否则,当前线程数过多,就把 他推入到等待队列中。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
- 将等待队列的线程调入到运行队列
可以看到在线程的执行的时候最后都调用了, client.dispatcher().finished(this);
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
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);
}
}
进入finish方法的底层,可以看到call 在runningAsyncCalls 队列中被移除了(calls.remove(call),并重新计算了目前正在执行 线程数量,如果为零执行idle。此外最重要的是通过promoteCalls进行任务队列的调度。
- 这里注意到remove和promotcalls的调用都加入了synchronized 同步锁(其他试图访问该代码块的线程会被阻塞),原因在步骤3中解释。
private void finished(Deque 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();
}
}
- promoteCalls任务队列调度:
经过简单的判断后,首先remove方法,将线程移出等待队列,再通过add加入运行队列。
- 最开始定义过runningAsyncCalls和readyAsyncCalls为双端队列,其在插入和删除的时候是非线程安全的,因此在调用他的时候(步骤2)加入了同步锁
- 通过循环也可以知道,每次任务调度的时候迭代器试图要遍历整个队列,直到运行队列满时才return。这样做要比来回传递计数变量(运行队列还能添加几个进程)来的简单安全。
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
最后
总而言之,成功是留给准备好的人的。无论是参加什么面试,都要做好充足的准备,注意好面试的礼仪和穿着,向面试官表现出自己的热忱与真诚就好。即使最后没有过关,也要做好经验的总结,为下一次面试做好充足准备。
这里我为大家准备了一些我在面试后整理的面试专题资料,除了面试题,还总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料分享给大家,希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
毕竟不管遇到什么样的面试官,去面试首先最主要的就是自己的实力,只要实力够硬,技术够强,就不怕面试拿不到offer!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
存中…(img-4WIaU5Ve-1715679682176)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!