Android线程池(一)

137 阅读4分钟

「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

一、为什么使用线程池

1.线程池吃好处
(1).对多个线程进行统一管理,避免资源竞争中出现问题
(2).(重点)对线程进行复用,线程在执行完任务后不会立刻销毁,而会等待另外的任务,这样就不会频繁的创建、销毁线程和调用GC
2.线程池适用的场景
(1)在项目中频繁的开启线程,需要多线程去处理不同的任务
(2)需要监控线程的运行状态
3.线程池运行规则
(1)如果线程池中的数量未达到核心线程的数量,则会直接启动一个核心线程来执行任务
(2)如果线程池中的数量已经达到或超过核心线程数,则任务会被插入到任务队列中等待执行
(3)如果(2)中的任务无法插入到任我队列中,由于任务队列已满,这时候如果线程数量未达到线程池规定的最大值,则会启动一个非核心线程来执行
(4)如果(3)中线程数量已达到线程池最大值,则会拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法通知调用者

二、线程池的实现ThreadPoolExecutor

1.构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                    BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

参数的含义:
corePoolSize: 线程池中核心线程的数量。

maximumPoolSize: 线程池中最大线程数量。

keepAliveTime: 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长。

unit: keepAliveTime这个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等。

workQueue: 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。

threadFactory: 为线程池提供创建新线程的功能,这个我们一般使用默认即可。

handler: 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。

2.两个执行的方法
(1)execute()方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //获得当前线程的生命周期对应的二进制状态码
        int c = ctl.get();
        //判断当前线程数量是否小于核心线程数量,如果小于就直接创建核心线程执行任务,创建成功直接跳出,失败则接着往下走.
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //判断线程池是否为RUNNING状态,并且将任务添加至队列中.
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //审核下线程池的状态,如果不是RUNNING状态,直接移除队列中
            if (! isRunning(recheck) && remove(command))
                reject(command);
                //如果当前线程数量为0,则单独创建线程,而不指定任务.
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果不满足上述条件,尝试创建一个非核心线程来执行任务,如果创建失败,调用reject()方法.
        else if (!addWorker(command, false))
            reject(command);
    }

(2)submit()方法

public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        //还是通过调用execute
        execute(ftask);
        //最后会将包装好的Runable返回
        return ftask;
    }

//将Callable<T> 包装进FutureTask中
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

//可以看出FutureTask也是实现Runnable接口,因为RunableFuture本身就继承了Runnabel接口
public class FutureTask<V> implements RunnableFuture<V> {
    .......
}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

(3)结论
从上面两个方法的源码我们可以分析出几个结论,

submit()其实还是需要调用execute()去执行任务的,不同是submit()将包装好的任务进行了返回,他会返回一个Future对象。
从execute()方法中,不难看出addWorker()方法, 是创建线程(核心线程,非核心线程)的主要方法,而reject()方法为线程创建失败的回调。
所以,通常情况下,在不需要线程执行返回结果值时,我们使用execute 方法。 而当我们需要返回值时,则使用submit方法,他会返回一个Future对象。Future不仅仅可以获得一个结果,他还可以被取消,我们可以通过调用future的cancel()方法,取消一个Future的执行。 比如我们加入了一个线程,但是在这过程中我们又想中断它,则可通过sumbit 来实现。