okHttp3 线程池

先来做一个简单的网络请求

Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

我们可以看到请求中使用了 client.newCall

  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

在 newCall 里其实返回的是 RealCall,所以 client.newCall(request).enqueue() 其实是调用 RealCall 的 enqueue 方法

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

在 client.dispatcher().enqueue() 时传入了 AsyncCall 对象,AsyncCall 是 RealCall 的内部类,实际上是一个 Runnable;

final class AsyncCall extends NamedRunnable

所以我们回头看下 client.dispatcher().enqueue(Runnable) 这个方法得处理

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      // 在运行队列 runningAsyncCalls 添加任务
      runningAsyncCalls.add(call);
      // 这里就是线程池的处理
      executorService().execute(call);
    } else {
      // 在等待队列 readyAsyncCalls 添加任务
      readyAsyncCalls.add(call);
    }
  }

其中 executorService() 这个方法是创建线程池的方法,这里用到了 ThreadPoolExecutor,可以看和 newCachedThreadPool 线程池的处理基本一致,可以看下 ThreadPoolExecutor 线程池原理

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

SynchronousQueue 这个等待队列里是没有任务,每一个插入操作(offer)必须等待另一个线程对应的移除操作(poll),反之亦然;

在上面有个判断 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) 如果这个不成立的时候是要把任务放在 readyAsyncCalls 里,那么我们什么时候去取这里的任务来执行呢,看下刚才说的 runnable 就是 AsyncCall

  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        // 网络请求的实际操作
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          // onFailure 回调
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          // onResponse 回调
          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);
      }
    }
  }

看到 finally 有个 runnable 结束的处理 client.dispatcher().finished(this); 看下里面的 finished 方法

  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
  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();
    }
  }

处理的地方在 promoteCalls 这个方法里

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    // 遍历 readyAsyncCalls,把任务取出来
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        // 取出来的任务加入 runningAsyncCalls
        runningAsyncCalls.add(call);
        // 把任务放入线程池
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

所以在请求任务数大于 maxRequests 并且相同 host 最大请求数大于 maxRequestsPerHost,就会把请求任务放在 readyAsyncCalls 队列里,当线程池里执行任务的 runnable 执行完任务在最后会检查 readyAsyncCalls 里有没有任务,如果有任务并且是同一个 host 就放入到线程池中执行,所以就是通过这个方法不断地从 readyAsyncCalls 队列里取出任务,对线程池里的线程进行复用。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值