OKHttp3源码解析(三)OKHttp的任务调度Dispatcher

继OKHttp3源码解析(一)同步请求的源码分析

继OKHttp3源码解析(二)同步请求的源码分析

什么是dispatcher?

dispatcher的作用是维护同步和异步的请求状态,并且内部维护一个线程池,用于执行相应的请求。

okHttp如何实现同步异步请求?

发送的同步/异步请求都会在dispatcher中管理其状态

接下来看一下dispatcher源码

看一下同步请求dispather.execute()是如何实现的

synchronized void executed(RealCall call) {
    this.runningSyncCalls.add(call);
}

直接的会把当前的请求放到同步队列当中,并没有向异步请求有限制
接下来看一下异步请求是如何实现的?

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

}

当前正在运行的异步数量是否超过64或者相同主机请求数量是否超过5,如果满足的话放到正在执行的异步队列中,并且交给线程池去处理这个请求,否则放到就绪队列当中去,进行缓存等待的操作

public synchronized ExecutorService executorService() {
    if (this.executorService == null) {
        this.executorService = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }

    return this.executorService;
}

ThreadPoolExecutor方法第一个参数设置为0有什么意义呢?其实是为了销毁全部线程,第二个参数表示最大的线程数,理论上可以无限扩大线程,但是由于OKHttp请求限制,也就不能无限的创建线程,第三个参数,当我们的线程数大于我们的核心线程数的时候,多出来的最多存活时间也就是60秒
Call执行完肯定需要在runningAsyncCalls队列中移除这个线程。那么readyAsyncCalls队列中的线程在什么时候才会被执行呢?
由上一篇文章知道,异步请求的运行都是在RealCall中内部类AsyncCall中执行的Run方法,而execute()就是实现run方法的地方,接下来看一下代码

protected void execute() {
        boolean signalledCallback = false;

        try {
            Response response = RealCall.this.getResponseWithInterceptorChain();
            if (RealCall.this.retryAndFollowUpInterceptor.isCanceled()) {
                signalledCallback = true;
                this.responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
                signalledCallback = true;
                this.responseCallback.onResponse(RealCall.this, response);
            }
        } catch (IOException var6) {
            if (signalledCallback) {
                Platform.get().log(4, "Callback failure for " + RealCall.this.toLoggableString(), var6);
            } else {
                this.responseCallback.onFailure(RealCall.this, var6);
            }
        } finally {
            RealCall.this.client.dispatcher().finished(this);   //不管到底有木有异常都会执行finished这个方法,前面的方法都是对respose成功失败的回调
        }

    }

接下来看一finish到底都做了一些什么操作

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) {//调整我们的任务队列,不管是同步还是异步请求当前的线程都是不安全的,所以加了一个同步锁给锁住
            this.promoteCalls();//①这个方法到底做了什么?
        }

        runningCallsCount = this.runningCallsCount();//重新计算了目前正在执行的线程数量
        idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
        idleCallback.run();
    }

}

接下来看一下promoteCalls()都干了什么?

private void promoteCalls() {
    if (this.runningAsyncCalls.size() < this.maxRequests) {//判断当前的正在执行的异步任务数量是否小于最大数量
        if (!this.readyAsyncCalls.isEmpty()) {//如果正在就绪等待的异步任务数量不是空的
            Iterator i = this.readyAsyncCalls.iterator(); //就会遍历这个就绪等待的异步请求队列

            do {
                if (!i.hasNext()) {
                    return;
                }

                AsyncCall call = (AsyncCall)i.next();
                if (this.runningCallsForHost(call) < this.maxRequestsPerHost) {
                    i.remove();//它会把这个就绪等待的异步请求删除
                    this.runningAsyncCalls.add(call);//然后添加到正在执行的异步队列当中
                    this.executorService().execute(call);//然后通过线程池去执行这个请求
                }
            } while(this.runningAsyncCalls.size() < this.maxRequests);

        }
    }
}

总结:每当有新的异步请求到Dispatcher这个管理类来的时候,通过调用call.enqueue(),通过判断,当前正在运行的异步数量是否超过64或者相同主机请求数量是否超过5,如果没有超出就会正常放到正在运行的异步队列当中去执行,如果超出数量的话,那么就会把当前请求放到就绪的队列当中去。等运行队列有空间了,再把就绪队列中的异步请求,放到正在执行的异步队列中来执行,当任务执行完成,它会调用finish中的promoteCall()调度任务的方法,然后判断当前正在执行的异步队列当中是否还有位置,有的话就会把就绪等待的异步任务放到正在执行的异步队列中,然后通过线程池去执行。

接下来会继续研究一下OKHttp的拦截器是如何操作的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万子开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值