OkHttp(3)的线程池

OkHttp 中的对所有的任务采用 NamedRunnable,约束每个执行单元给出对应的业务名称,以便于线程维护

1.异步请求线程池-OkHttp Dispatcher

  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;
  }
  • 该线程池与Android下的 Executors.newCachedThreadPool() 比较类似;
  • 无任务上限,自动回收闲置60s的线程,适用于大量耗时较短的任务;
  • 虽然线程池无任务上限,但是Dispatcher对入口enqueue()进行了把关,最大的异步任务数默认是64,同一个主机默认是5,当然这两个默认值是可以修改的,Dispatcher提供的修改接口;
okHttpClient.dispatcher().setMaxRequests(128);
okHttpClient.dispatcher().setMaxRequestsPerHost(10);
  • 通过两个双端队列来维护准备执行的任务和正在执行的任务:Deque< AsyncCall> readyAsyncCalls, Deque< AsyncCall> runningAsyncCalls;

  • 在每个任务结束时,都会检查 readyAsyncCalls 是否有任务,在条件满足的情况下,按照先进先出的原则将任务移动到 runningAsyncCalls中,并在线程池中执行;

2.连接池清理线程池-OkHttp ConnectionPool

ConnectionPool 即连接池,用来管理 HTTP 和 HTTP/2 连接的重用,以减少网络延迟。

相同的 HTTP 请求可以共用一个连接(RealConnection), ConnectionPool 实现了将哪些连接保持打开状态以备将来使用的策略。

即 ConnectionPool 是用来管理 RealConnection 用的。

2.1 ConnectionPool 的成员变量

1.用于清除过期的连接,每个连接池最多只能运行一个线程。
线程池执行程序允许对池本身进行垃圾回收。

private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
    Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));

2.最大空闲连接数,在cleanup(...)清理的时候用到

private final int maxIdleConnections;

3.每个空闲连接的存活时间的纳秒数

private final long keepAliveDurationNs;

4.是否是清理过期连接的标记位

boolean cleanupRunning;

5.清除过期连接的任务

private final Runnable cleanupRunnable = new Runnable() {
  @Override public void run() {
    while (true) {
      long waitNanos = cleanup(System.nanoTime());
      if (waitNanos == -1) return;
      if (waitNanos > 0) {
        long waitMillis = waitNanos / 1000000L;
        waitNanos -= (waitMillis * 1000000L);
        synchronized (ConnectionPool.this) {
          try {
            ConnectionPool.this.wait(waitMillis, (int) waitNanos);
          } catch (InterruptedException ignored) {
          }
        }
      }
    }
  }
};

6.保存可复用连接的队列

private final Deque<RealConnection> connections = new ArrayDeque<>();

2.2 成员方法

1.void put(RealConnection connection)
先清除过期的连接,再将可复用的连接保存到队列connections

void put(RealConnection connection) {
  assert (Thread.holdsLock(this));
  if (!cleanupRunning) {
    cleanupRunning = true;
    executor.execute(cleanupRunnable);
  }
  connections.add(connection);
}

2.long cleanup(long now)
清除过期的空闲连接。

  1. 循环遍历connections 主要是寻找队列中 正在使用的连接inUseConnectionCount、空闲连接的个数idleConnectionCount、空闲等待最久的连接longestIdleConnection
  2. 空闲等待最久的连接等待时间超过了keepAliveDurationNs,或者 空闲连总数大于了允许的最大空闲连接数maxIdleConnections,则从队列中移除当前连接,并关闭,然后cleanupRunnable继续执行 cleanup(long now).
  3. 如果有空闲的连接,则cleanupRunnable等待时间为keepAliveDurationNs - longestIdleDurationNs
  4. 如果连接都在使用中,则cleanupRunnable等待时间为keepAliveDurationNs
  5. 否则cleanupRunnable任务完成
long cleanup(long now) {
  int inUseConnectionCount = 0;
  int idleConnectionCount = 0;
  RealConnection longestIdleConnection = null;
  long longestIdleDurationNs = Long.MIN_VALUE;
  synchronized (this) {
    // 1.0
    for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
      RealConnection connection = i.next();
      if (pruneAndGetAllocationCount(connection, now) > 0) {
        inUseConnectionCount++;
        continue;
      }
      idleConnectionCount++;
      long idleDurationNs = now - connection.idleAtNanos;
      if (idleDurationNs > longestIdleDurationNs) {
        longestIdleDurationNs = idleDurationNs;
        longestIdleConnection = connection;
      }
    }
    if (longestIdleDurationNs >= this.keepAliveDurationNs
        || idleConnectionCount > this.maxIdleConnections) { //2.0
      connections.remove(longestIdleConnection);
    } else if (idleConnectionCount > 0) {//3.0
      return keepAliveDurationNs - longestIdleDurationNs;
    } else if (inUseConnectionCount > 0) {//4.0
      return keepAliveDurationNs;
    } else {//5.0
      cleanupRunning = false;
      return -1;
    }
  }
  closeQuietly(longestIdleConnection.socket());//关闭连接
  return 0;
}

在这里插入图片描述

3.缓存整理线程池-OkHttp DiskLruCache

// Use a single background thread to evict entries.
Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp DiskLruCache", true));

该线程池用于整理本地请求缓存数据;
缓存的整理包含: 达到阀值大小的文件,删除最近最少使用的记录,在有关操作达到一定数量以后对记录进行重建;
最大运行线程数1,无需考虑线程安全问题,自动回收闲置60s的线程;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值