OkHttp3笔记---TaskRunner

    TaskRunner为OkHttp中的网络通信创建线程,它使用一个线程池,创建线程用以处理OkHttp交付的网络通信任务。

一、TaskRunner概览

    BackEnd:网络任务的处理器,用于处理网络任务。它包含了一个线程池,此线程池的核心线程数量为0,最大线程数量为Integer.MAX_VALUE,任务队列为一个容量为0的阻塞队列。

    Task:网络任务

    TaskQueue:是一个任务队列,包含了多个网络任务,对网络任务进行管理

    readyQueues:就绪任务队列所在的队列,包含了多个已就绪的任务队列

    busyQueues:正在执行的任务队列所在的队列

    TaskRunner的执行流程

    1、初始化线程池。此线程池的核心线程数量为0,最大线程数量为Integer.MAX_VALUE,任务队列为一个容量为0的阻塞队列。

    2、寻找可执行的任务,并执行。在readyQueues中寻找可以执行的任务,将任务所在的队列转移到busyQueues中,并执行任务。若找到多个可执行的任务,则会利用线程池新建一个线程,加快任务的处理。

    3、处理已执行完成的任务。根据任务的返回值,判断任务是否需要再次处理(返回值为-1,则不需要)。将结束任务所在的队列转移到readyQueues中。

    4、循环执行2、3

二、TaskRunner代码分析

    1、初始化线程池

companion object {
    @JvmField
    val INSTANCE = TaskRunner(RealBackend(threadFactory("$okHttpName TaskRunner", daemon = true)))

    val logger: Logger = Logger.getLogger(TaskRunner::class.java.name)
  }

......

class RealBackend(threadFactory: ThreadFactory) : Backend {
    private val executor = ThreadPoolExecutor(
        0, // corePoolSize.
        Int.MAX_VALUE, // maximumPoolSize.
        60L, TimeUnit.SECONDS, // keepAliveTime.
        SynchronousQueue(),
        threadFactory
    )

    可以看出实例化TaskRunner时会传入一个RealBackEnd对象,此对象包含一个线程池

    2、提交网络任务

  fun start(sendConnectionPreface: Boolean = true, taskRunner: TaskRunner = TaskRunner.INSTANCE) {
    if (sendConnectionPreface) {
      writer.connectionPreface()
      writer.settings(okHttpSettings)
      val windowSize = okHttpSettings.initialWindowSize
      if (windowSize != DEFAULT_INITIAL_WINDOW_SIZE) {
        writer.windowUpdate(0, (windowSize - DEFAULT_INITIAL_WINDOW_SIZE).toLong())
      }
    }
    // Thread doesn't use client Dispatcher, since it is scoped potentially across clients via
    // 提交网络任务
    taskRunner.newQueue().execute(name = connectionName, block = readerRunnable)
  }

    在Okhttp中,Http2Connection的start方法会提交一个网络任务readerRunnable。用来读取服务端返回的数据帧,大体看下readerRunnable的实现。

inner class ReaderRunnable internal constructor(
    internal val reader: Http2Reader
  ) : Http2Reader.Handler, () -> Unit {
    override fun invoke() {
      var connectionErrorCode = ErrorCode.INTERNAL_ERROR
      var streamErrorCode = ErrorCode.INTERNAL_ERROR
      var errorException: IOException? = null
      try {
        reader.readConnectionPreface(this)
        //循环读取服务端的数据帧
        while (reader.nextFrame(false, this)) {
        }
        connectionErrorCode = ErrorCode.NO_ERROR
        streamErrorCode = ErrorCode.CANCEL
      } catch (e: IOException) {
        errorException = e
        connectionErrorCode = ErrorCode.PROTOCOL_ERROR
        streamErrorCode = ErrorCode.PROTOCOL_ERROR
      }

    2.1 新建网络任务队列

    在提交网络任务时,会调用newQueue方法新建一个网络任务队列,一般来说,在OkHttp中一个网络任务队列只包含一个网络任务。newQueue方法如下:

  fun newQueue(): TaskQueue {
    val name = synchronized(this) { nextQueueName++ }
    return TaskQueue(this, "Q$name")
  }

    2.2 提交网络任务到线程池

    在新建网络任务队列,调用其的execute方法后,会将任务插入到任务队列中,并将任务队列添加到readyQueue。execute方法代码如下:

inline fun execute(
    name: String,
    delayNanos: Long = 0L,
    cancelable: Boolean = true,
    crossinline block: () -> Unit
  ) {
    schedule(object : Task(name, cancelable) {
      override fun runOnce(): Long {
        block()  //循环读取服务端的数据帧
        return -1L //表示任务执行完成后,不需要再执行
      }
    }, delayNanos)
  }

    接下来会调用schedule方法,将任务添加到任务队列,并发出通知,告知TaskRunner去执行网络任务。schedule方法代码如下:

fun schedule(task: Task, delayNanos: Long = 0L) {
    synchronized(taskRunner) {
      if (shutdown) {
        if (task.cancelable) {
          taskLog(task, this) { "schedule canceled (queue is shutdown)" }
          return
        }
        taskLog(task, this) { "schedule failed (queue is shutdown)" }
        throw RejectedExecutionException()
      }
      //添加网络任务
      if (scheduleAndDecide(task, delayNanos, recurrence = false)) {
        taskRunner.kickCoordinator(this)  //通知taskRunner去处理网络任务
      }
    }
  }

    scheduleAndDecide方法会添加一个网络任务,具体代码如下:

internal fun scheduleAndDecide(task: Task, delayNanos: Long, recurrence: Boolean): Boolean {
    //将task与任务队列关联
    task.initQueue(this)
    // 计算task的执行时刻
    val now = taskRunner.backend.nanoTime()
    val executeNanoTime = now + delayNanos

    // task已经加入任务队列,且执行时刻早于当前的执行时刻,则直接返回
    val existingIndex = futureTasks.indexOf(task)
    if (existingIndex != -1) {
      if (task.nextExecuteNanoTime <= executeNanoTime) {
        taskLog(task, this) { "already scheduled" }
        return false
      }
      //之前添加的task执行时刻较晚,则删除之前的任务
      futureTasks.removeAt(existingIndex) // Already scheduled later: reschedule below!
    }
    ......

    // 按照执行时刻(从早到晚排序),将任务添加到任务队列中
    var insertAt = futureTasks.indexOfFirst { it.nextExecuteNanoTime - now > delayNanos }
    if (insertAt == -1) insertAt = futureTasks.size
    futureTasks.add(insertAt, task)

    //若插入到队列头,表示有新的任务了,提醒taskRunner去执行
    return insertAt == 0
  }

    一般情况下,添加完一个新的task之后,scheduleAndDecide方法会返回true,则接下来执行kickCoordinator方法

internal fun kickCoordinator(taskQueue: TaskQueue) {
    this.assertThreadHoldsLock()

    if (taskQueue.activeTask == null) {
      if (taskQueue.futureTasks.isNotEmpty()) {
        //将之前新建的任务队列放到readyQueues中
        readyQueues.addIfAbsent(taskQueue)
      } else {
        readyQueues.remove(taskQueue)
      }
    }
    
    if (coordinatorWaiting) {
      //coordinatorWaiting初始化为false,此步为唤醒无任务可以处理的等待线程
      backend.coordinatorNotify(this@TaskRunner)
    } else {
      //启动新的线程,执行runnable中的代码,以处理网络任务
      backend.execute(runnable)
    }
  }

 

    2.3 启动线程处理任务

    在上一步,最后会调用backend的execute方法启动一个线程去处理网络任务,代码如下:

private val runnable: Runnable = object : Runnable {
    override fun run() {
      while (true) {
        //获取就绪的任务
        val task = synchronized(this@TaskRunner) {
          awaitTaskToRun()
        } ?: return

        logElapsed(task, task.queue!!) {
          var completedNormally = false
          try {
            //执行网络任务
            runTask(task)
            completedNormally = true
          } finally {
            // If the task is crashing start another thread to service the queues.
            if (!completedNormally) {
              backend.execute(this)
            }
          }
        }
      }
    }
  }

override fun execute(runnable: Runnable) {
      //将runnable添加到线程池
      executor.execute(runnable)
    }

    RealBackEnd的execute方法会将runnable添加到线程池中去执行,在执行时,会先调用awaitTaskToRun去获取一个可执行的任务,awaitTaskToRun方法的代码如下:

fun awaitTaskToRun(): Task? {
    this.assertThreadHoldsLock()

    while (true) {
      if (readyQueues.isEmpty()) {
        return null // Nothing to do.
      }

      val now = backend.nanoTime()
      var minDelayNanos = Long.MAX_VALUE
      var readyTask: Task? = null
      var multipleReadyTasks = false

      // 决定执行哪个任务. 此循环的目标如下:
      //  * 若有任务,当前线程执行任务;若无任务,当前线程睡眠
      //  * 若有2个以上的可执行任务,启动另一个线程,加快任务的处理
      eachQueue@ for (queue in readyQueues) {
        val candidate = queue.futureTasks[0]
        val candidateDelay = maxOf(0L, candidate.nextExecuteNanoTime - now)

        when {
          // 根据执行时刻,判断任务是否可执行
          candidateDelay > 0L -> {
            minDelayNanos = minOf(candidateDelay, minDelayNanos)
            continue@eachQueue
          }

          // 已找到2个任务了,停止寻找可执行任务,设置标志以启动另一个线程
          readyTask != null -> {
            multipleReadyTasks = true
            break@eachQueue
          }

          // 记录可执行任务
          else -> {
            readyTask = candidate
          }
        }
      }

      // Implement the decision.
      when {
        // We have a task ready to go. Get ready.
        readyTask != null -> {
          //将任务队列转移到busyQueues
          beforeRun(readyTask)

          // 启动另一个线程,加快任务的处理
          if (multipleReadyTasks || !coordinatorWaiting && readyQueues.isNotEmpty()) {
            backend.execute(runnable)
          }
          //返回一个可以执行的任务
          return readyTask
        }

        // Notify the coordinator of a task that's coming up soon.
        coordinatorWaiting -> {
          if (minDelayNanos < coordinatorWakeUpAt - now) {
            backend.coordinatorNotify(this@TaskRunner)
          }
          return null
        }

        // 无可执行任务,进行等待,并设置等待时间
        else -> {
          coordinatorWaiting = true
          coordinatorWakeUpAt = now + minDelayNanos
          try {
            backend.coordinatorWait(this@TaskRunner, minDelayNanos)
          } catch (_: InterruptedException) {
            // Will cause all tasks to exit unless more are scheduled!
            cancelAll()
          } finally {
            coordinatorWaiting = false
          }
        }
      }
    }
  }

    接下来,获取到任务后,就可以调用runTask方法进行执行

 

private fun runTask(task: Task) {
    this.assertThreadDoesntHoldLock()

    val currentThread = Thread.currentThread()
    val oldName = currentThread.name
    currentThread.name = task.name

    var delayNanos = -1L
    try {
      delayNanos = task.runOnce()
    } finally {
      synchronized(this) {
        //任务执行完成后,将任务队列转移到readyQueues
        afterRun(task, delayNanos)
      }
      currentThread.name = oldName
    }
  }

    至此,TaskRunner任务处理流程结束。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值