O uso e o empacotamento do pool de threads do Android ThreadPoolExecutor (kotlin)

// 传统开启线程方式
Thread(Runnable {
     //to do异步请求

}).start()

1. Problemas com a criação de threads usando novo Thread ()    

1> Se um Thread for criado para cada item em uma lista, um grande número de Threads será criado se a lista for grande, resultando em instabilidade de memória e reciclagem frequente de GC. Você sabe, a coleção de GC está no thread principal, o que causará lag.

2> Muitos threads fazem com que cada thread concorra pelos direitos de execução da CPU, e a troca frequente de threads leva a uma diminuição na eficiência.

3> Cada item de ListView desliza para fora da janela, e o thread não pode ser interrompido ou controlado.

 

2. Benefícios do uso de pool de threads

1. Reutilize os tópicos bons já criados para evitar GC frequente causado pela criação frequente

2. Controle o número de threads simultâneos, use os recursos do sistema de maneira razoável e melhore o desempenho do aplicativo

3. Pode controlar efetivamente a execução de threads, como cronometrar a execução, cancelar a execução, etc.

 

3. Crie um thread pool ThreadPoolExecutor 7 parâmetros

O número de threads principais no pool de threads corePoolSize  

maximumPoolSize O   número máximo de threads no pool de threads

KeepAliveTime é a duração do tempo limite de threads não principais. Quando o tempo ocioso de threads não principais no sistema exceder keepAliveTime, eles serão reciclados. Se a propriedade allowCoreThreadTimeOut de ThreadPoolExecutor for definida como verdadeira, este parâmetro também representará a duração do tempo limite do thread principal

unidade A unidade do terceiro parâmetro, incluindo nanossegundos, microssegundos, milissegundos, segundos, minutos, horas, dias, etc.

A fila de tarefas no conjunto de encadeamentos workQueue , que é usado principalmente para armazenar tarefas que foram enviadas, mas ainda não executadas. As tarefas armazenadas aqui são enviadas pelo método execute de ThreadPoolExecutor.

threadFactory   fornece a função de criar novos threads para o pool de threads, geralmente usamos o padrão

Estratégia de rejeição do manipulador . Quando um thread não pode executar uma nova tarefa (geralmente porque o número de threads no pool de threads atingiu o máximo ou o pool de threads está fechado), por padrão, quando o pool de threads não pode lidar com um novo thread, ele lançará um RejectedExecutionException.
 

3. Método ThreadPoolExecutor do pool de threads

1> shutDown ()   fecha o pool de threads sem afetar as tarefas enviadas

2> shutDownNow ()  fecha o pool de threads e tenta encerrar a thread em execução

3> allowCoreThreadTimeOut (valor booleano)  permite que os threads principais sejam reciclados quando o tempo limite for inativo

 

3. Classe de pacote de pool de threads



import com.orhanobut.logger.Logger
import java.util.concurrent.*

/***
 *      Created by LiangJingJie on 2019/5/16.
 *      线程池封装类
 * */
class ThreadPoolManager private constructor() {

    private var threadPoolMap = hashMapOf<String, ThreadPoolExecutor>()

    /**
     * cpu数量
     * */
    private val CPU_COUNT = Runtime.getRuntime().availableProcessors()

    /**
     * 核心线程数为手机CPU数量+1
     * */
    private val CORE_POOL_SIZE = CPU_COUNT + 1

    /**
     * 最大线程数为手机CPU数量×2+1
     * */
    private val MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1

    /**
     * 线程活跃时间 秒,超时线程会被回收
     * */
    private val KEEP_ALIVE_TIME: Long = 3

    /**
     * 等待队列大小
     * */
    private val QUEUE_SIZE = 128

    companion object {
        fun getInstance() = SingleHolder.SINGLE_HOLDER
    }

    object SingleHolder {
        val SINGLE_HOLDER = ThreadPoolManager()
    }


    /**
     *   @param tag 针对每个TAG 获取对应的线程池
     *   @param corePoolSize  线程池中核心线程的数量
     *   @param maximumPoolSize  线程池中最大线程数量
     *   @param keepAliveTime 非核心线程的超时时长,
     *   当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收
     *   如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
     *   @param unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等
     *   @param queueSize 等待队列的长度 一般128 (参考 AsyncTask)
     *   workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
     *   threadFactory  为线程池提供创建新线程的功能,这个我们一般使用默认即可
     *
     *   1.ArrayBlockingQueue:这个表示一个规定了大小的BlockingQueue,ArrayBlockingQueue的构造函数接受一个int类型的数据,
     *              该数据表示BlockingQueue的大小,存储在ArrayBlockingQueue中的元素按照FIFO(先进先出)的方式来进行存取。
     *   2.LinkedBlockingQueue:这个表示一个大小不确定的BlockingQueue,在LinkedBlockingQueue的构造方法中可以传
     *          一个int类型的数据,这样创建出来的LinkedBlockingQueue是有大小的,也可以不传,不传的话,
     *          LinkedBlockingQueue的大小就为Integer.MAX_VALUE
     * */
    private fun getThreadPool(tag: String): ThreadPoolExecutor {
        var threadPoolExecutor = threadPoolMap[tag]
        if (threadPoolExecutor == null) {
            threadPoolExecutor = ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                ArrayBlockingQueue<Runnable>(QUEUE_SIZE),
                Executors.defaultThreadFactory(),
                RejectedExecutionHandler { _, _ ->
                    Logger.d("$ThreadPoolManager  RejectedExecutionHandler----")
                }
            )
            //允许核心线程闲置超时时被回收
            threadPoolExecutor.allowCoreThreadTimeOut(true)
            threadPoolMap[tag] = threadPoolExecutor
        }
        return threadPoolExecutor
    }

    /**
     *  @param tag 针对每个TAG 获取对应的线程池
     *  @param runnable 对应的 runnable 任务
     * */
    fun removeTask(tag: String, runnable: Runnable) {
        getThreadPool(tag)?.queue?.remove(runnable)
    }

    /**
     *  @param tag 针对每个TAG 获取对应的线程池
     *  @param runnable 对应的 runnable 任务
     * */
    fun addTask(tag: String, runnable: Runnable) {
        getThreadPool(tag).execute(runnable)
    }

    /**
     *   @param tag 针对每个TAG 获取对应的线程池
     *   取消 移除线程池
     * */

    //shutDown():关闭线程池后不影响已经提交的任务
    //shutDownNow():关闭线程池后会尝试去终止正在执行任务的线程
    fun exitThreadPool(tag: String) {
        var threadPoolExecutor = threadPoolMap[tag]
        if (threadPoolExecutor != null) {
            threadPoolExecutor.shutdownNow()
            threadPoolMap.remove(tag)
        }
    }
}

 

A ideia geral da classe de pacote :

1> Use um hashMap para armazenar cada pool de threads correspondente a TAG

2> Adicionar tarefa de thread por meio de addTask (tag: String, runnable: Runnable)

3> Remover tarefa de thread por removeTask (tag: String, runnable: Runnable)

      Isso deve ser explicado primeiro. A API ThreadPoolExecutor do sistema  não fornece uma interface para remover tarefas. No método removeTask (), a fila correspondente BlockingQueue é obtida através de ThreadPoolExecutor , e a tarefa de thread Runnable é removida da fila. Como este BlockingQueue é  o ArrayBlockingQueue transmitido pelo construtor quando criamos a  instância ThreadPoolExecutor , adicionaremos tarefas por meio do pool de threads posteriormente e todas serão colocadas nesta fila. As tarefas de execução do pool de threads serão retiradas desta fila. O teste Pro não é problema. Se você tiver alguma dúvida, por favor diga ao meu irmão

 

OK, agora desta vez corremos para executar o código

[ Cenário 1 O  número de threads principais é maior do que o número de tarefas ]  

          Definimos o número de threads principais CORE_POOL_SIZE em 20, o número máximo de threads MAXIMUM_POOL_SIZE em 30 e a fila de espera mantém 128

Em seguida, adicione 10 tarefas. Obviamente, as tarefas são executadas todas de uma vez.

// 创建十个任务
        for (i in 1..10) {
            val runnable = Runnable {
                val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss:ms")
                val time = sdf.format(Date(java.lang.Long.parseLong(System.currentTimeMillis().toString())))
                Logger.d("ThreadPoolManager  Runnable ---- $i time: $time" )
            }
            ThreadPoolManager.getInstance().addTask("TAG", runnable)
        }

Olha o log e ele é executado em seqüência, olha o tempo, ele é executado quase ao mesmo tempo

 

[ Cenário 2 O  número de threads principais é menor que o número de tarefas ]

Definimos o número de threads principais CORE_POOL_SIZE para 3 e o número máximo de threads MAXIMUM_POOL_SIZE para 5, e a fila de espera mantém 128. Para simular as operações demoradas executadas por threads, cada thread dorme por 5 segundos após a execução da função.

// 创建十个任务
        for (i in 1..10) {
            val runnable = Runnable {
                val sdf = SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒")
                val time = sdf.format(Date(java.lang.Long.parseLong(System.currentTimeMillis().toString())))
                Logger.d("ThreadPoolManager  Runnable ---- 任务$i   执行时间是: $time")
                //模拟耗时操作,睡眠5秒
                Thread.sleep(5000)
            }
            ThreadPoolManager.getInstance().addTask("TAG", runnable)
        }

 

Veja os resultados impressos: os primeiros 3 (1-3) serão executados primeiro, após 5 segundos, a seguir 3 (4-6) serão executados, após 5 segundos, a seguir três (7-9) serão executados e, finalmente, o 10º será executado

 

Analise o processo de pool de threads:

1> Como o número de tarefas é 10, o número máximo de threads é 5, a thread principal é apenas 3 e a fila de espera mantém 128.

2> Assim, ao adicionar as 3 primeiras tarefas à fila de tarefas, 3 threads principais serão criados primeiro para executar as três primeiras tarefas. Assim, as tarefas 1, 2 e 3 serão executadas primeiro.

3> Quando a quarta tarefa é adicionada ao pool de threads, porque a thread principal atingiu o limite superior, mas a fila de tarefas (máximo de 128) não foi preenchida, então não iniciará uma nova thread não principal para realizar a tarefa, mas Coloque a tarefa na fila de espera e espere até que haja threads principais ociosos para executar as tarefas na fila de espera.

4> Portanto, é a execução de 3 tarefas e 3 tarefas.

 

Quanto a outros cenários, as seguintes regras são seguidas:

1. Depois de executar um thread, se o número de threads no pool de threads não atingir o número de threads principais, um thread principal será imediatamente ativado para executar

2. Depois de executar um encadeamento, se o número de encadeamentos no pool de encadeamentos atingiu o número de encadeamentos principais e o workQueue não está cheio, o novo encadeamento é colocado no workQueue para aguardar a execução

3. Depois de executar um encadeamento, se o número de encadeamentos no pool de encadeamentos atingiu o número de encadeamentos principais, mas não mais do que o número de encadeamentos não principais e o workQueue está cheio, inicie um encadeamento não principal para realizar a tarefa

4. Depois de executar um thread, se o número de threads no pool de threads excedeu o número de threads não principais, recuse-se a executar a tarefa

 

Não vou mostrar a eles um por um.

 

[ Cena 3  remove a tarefa enviada ]

Nesse cenário, eu envio uma tarefa para o pool de threads, mas desejo cancelar a tarefa quando ela não for executada. Este cenário é muito comum quando cada item do RecycleView desliza rapidamente. Quando o item entra na janela, uma tarefa assíncrona deve ser executada, mas o item deslizará rapidamente para fora da tela antes de ser executado. Neste momento, a tarefa pode ser removida da fila do pool de threads. Pare de falar bobagem e vá para o código de simulação.

Definimos o número de threads principais CORE_POOL_SIZE para 3 e o número máximo de threads MAXIMUM_POOL_SIZE para 5, e a fila de espera mantém 128.

var runnable6: Runnable? = null
        var runnable7: Runnable? = null
        var runnable8: Runnable? = null
        var runnable9: Runnable? = null
        for (i in 1..10) {
            val runnable = Runnable {
                val sdf = SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒")
                val time = sdf.format(Date(java.lang.Long.parseLong(System.currentTimeMillis().toString())))
                Logger.d("ThreadPoolManager  Runnable ---- 任务$i   执行时间是: $time")
                //模拟耗时操作,睡眠5秒
                Thread.sleep(5000)
            }
            ThreadPoolManager.getInstance().addTask("TAG", runnable)
            if (i == 6) {
                runnable6 = runnable
            }
            if (i == 7) {
                runnable7 = runnable
            }
            if (i == 8) {
                runnable8 = runnable
            }
            if (i == 9) {
                runnable9 = runnable
            }
        }
        //添加后,立马移除
        ThreadPoolManager.getInstance().removeTask("TAG", runnable6!!)
        ThreadPoolManager.getInstance().removeTask("TAG", runnable7!!)
        ThreadPoolManager.getInstance().removeTask("TAG", runnable8!!)
        ThreadPoolManager.getInstance().removeTask("TAG", runnable9!!)

 

resultado da operação:

 

Analise o processo de pool de threads:

1> Como o número de tarefas é 10, o número máximo de threads é 5, a thread principal é apenas 3 e a fila de espera mantém 128.

2> Assim, ao adicionar as 3 primeiras tarefas à fila de tarefas, 3 threads principais serão criados primeiro para executar as três primeiras tarefas. Assim, as tarefas 1, 2 e 3 serão executadas primeiro.

3> Ao adicionar a tarefa 4 a 10 ao pool de threads, coloque a tarefa na fila de espera e espere.

4> Então, por meio do pool de threads atual, obtenha a fila de fila correspondente e remova a tarefa.

Este removeTask () é implementado assim:

/**
     *  @param tag 针对每个TAG 获取对应的线程池
     *  @param runnable 对应的 runnable 任务
     * */
    fun removeTask(tag: String, runnable: Runnable) {
        getThreadPool(tag)?.queue?.remove(runnable)
    }

Esta fila é o ArrayBlockingQueue () passado através da construção quando criamos a instância do pool de threads.Esta fila é usada para armazenar tarefas executáveis ​​aguardando para serem executadas. Excluído por meio desta fila não será executado.

Este código-fonte também pode ser verificado por meio do código-fonte:

ThreadPoolExecutor的构造方法:

 

Obtido através da fila

Ao posicionar o código, é obviamente a mesma variável.

 

 

O código acima não é problema no teste de pró. Se houver algum problema ou algo errado, por favor deixe uma mensagem de agradecimento.

 

Acho que você gosta

Origin blog.csdn.net/Leo_Liang_jie/article/details/90263072
Recomendado
Clasificación