OKHttp3源码分析<CacheThreadPool线程池异步请求任务的执行>

1.线程池的好处

几乎所有需要异步操作或者并发执行任务的程序都可以使用线程池,三个好处:

  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
  • 提高响应速度:当任务到达的时候,任务可以不需要等到现成的额创建就能立即执行;
  • 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统稳定性,使线程池可以进行统一的分配、调优和监控。

2.Okhttp3 线程池的配置

实际上是一个CacheThreadPool(核心线程为0,最大线程为Integer.MAX_VALUE),空闲线程等待60秒之后如果没有新任务的话,就终止进程。
适用于执行很多短期异步任务的小程序,或者负载比较轻的服务器.下面的代码就说明了:
Dispatcher.java

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

可以看出,在Okhttp中,构建了一个阀值为[0, Integer.MAX_VALUE]的线程池,它不保留任何最小线程数,随时创建更多的线程数,当线程空闲时只能活60秒,它使用了一个不存储元素的阻塞工作队列,一个叫做”OkHttp Dispatcher”的线程工厂。

也就是说,在实际运行中,当收到10个并发请求时,线程池会创建十个线程,当工作完成后,线程池会在60s后相继关闭所有线程。

我们在使用OKhttp3执行 异步请求任务
a)通过OkHttpClient对象实例调用newCall方法

 OkHttpClient client = new OkHttpClient();

        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) {
                System.out.println("请求失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                System.out.println("请求成功");
                System.out.println(response.body().string());
            }
        });

b).client.newCall(request)返回的是一个RealCall对象:
OkHttpClient.java

  /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

c).RealCall对象中的enqueue方法:
RealCall.java

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

这里你也许会有疑问,这个方法传递进去了Callback对象,实例化了一个AsyncCall对象(new AsyncCall(responseCallback)),那我们的请求将在哪里执行呢?
原来RealCall的构造函数中将请求对象赋值给了RealCall中的成员变量originalRequest

  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

然后再来看看AsyncCall类(这个类是RealCall类的内部类,所以这个类将可以得到RealCall的originalRequest ,也就是我们的请求),AsyncCall类继承于NamedRunnable类,NamedRunnable类是实现了Runnable接口的。
看一下NamedRunnable类的定义:

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

从代码中我们可以看到NamedRunnable 的子类会在其run()方法去执行execute()方法。
现在再回到AsyncCall(NamedRunnable 的子类)的execute()方法:

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

我们在代码中可以看到Response response = getResponseWithInterceptorChain();这行代码,这究竟是什么:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

这里我们终于知道了我们的请求最终是经过线程池的调用线程执行中的run(),然后run()调用execute()方法,接着execute()方法调用getResponseWithInterceptorChain(),最后我们的请求才得以处理。

d).再回到client.dispatcher()返回的是一个dispatcher对象,看看dispatcher对象中的enqueue()方法:
Dispatcher.java

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

当运行中的异步请求任务队列(runningAsyncCalls)的长度小于线程池的最大请求数(maxRequests),并且对同一个主机的请求数目小于初始化maxRequestsPerHost的时候,将请求加入运行中异步请求队列,并让线程池去执行这个请求

e).看看这行代码:executorService().execute(call);
这行代码会执行我们的请求,不明白的回去看c)
executorService()返回的是一个CacheThreadPool,这个线程池是一个大小无界的线程池,适用于执行很多的短期的异步任务的小程序,或者负载较轻的服务器。
这个线程池使用的阻塞队列是SynchronousQueue,这是一个没有容量的阻塞队列。每一个插入操作(offer)必须等待另一个线程对应的移除操作(poll),反之亦然。下图是CacheThreadPool中任务传递的示意图:
这里写图片描述
看看CacheThreadPool的execute方法的运行示意图:
这里写图片描述

1)首先执行SynchronousQueue.offer(Runnable task)。如果当前maximumPool中有空闲线程
正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行
offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方
法执行完成;否则执行下面的步骤2)。
2)当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行
SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤1)将失
败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。
3)在步骤2)中新创建的线程将任务执行完后,会执行
SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线
程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执
行步骤1)),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。由于
空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。

至此,使用OKhttp3发起一次异步请求的流程就走完了!

3.线程池是如何实现复用的

回到2.c)中的代码,

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

最后会执行client.dispatcher().finished(this);,进到Dispatcher类中的

/** Used by {@code AsyncCall#run} to signal completion. */
  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();
    }
  }

看到if (promoteCalls) promoteCalls();,进入 promoteCalls()方法

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();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

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

发现线程池中线程的复用正是在这里得到了复用,这个方法将readyAsyncCalls中的任务加到了runningAsyncCalls队列中,以复用已经空闲的线程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值