Android线程池—三大分析法分析线程池

三大分析法分析android线程池主要分为四部分,第一部分是4W2H分析线程池,第二部分是MECE分析线程池,第三部分是SCQA分析线程池,最后一部分是结语。

其中,4W2H分析线程池主要围绕线程池提出了6个高价值问题。

其中,MECE分析线程池主要分为线程池基本操作、线程池生命周期、线程池工作原理、线程池代码案例分析、线程池的性能优化、线程池注意事项、线程池线程数量确定和线程池业务防劣化8部分。

一、4W2H分析线程池

1. 线程池具体定义

在这里插入图片描述
线程池是一种管理和调度线程的机制,线程池可以控制线程的数量,确保线程有效地工作,并且可以重复使用以前创建的线程,从而减少系统的开销。

可以 点击 “此处” 即可 免费获取 学习资料 以及 更多Android学习笔记+源码解析+面试视频

2. 线程池使用原因

在这里插入图片描述
如果不使用线程池,每个任务都新开一个线程处理 for循环创建线程,开销太大,我们希望有固定数量的线程,来执行这1000个线程,就避免了反复创建并销毁线程所带来的开销问题

3. 线程池使用方式

3.1 线程池API文档

API 简介
isShutdown 判断当前线程的状态是否是SHUTDOWN,即是否调用了shutdown方法
isTerminating 当前线程池的状态是否小于TERMINATED,并且大于等于SHUTDOWN,即当前线程是否已经shutdown并且正在终止。
isTerminated 线程池是否终止完成
awaitTermination 等待直到线程状态变为TERMINATED
finalize 重新Object的方法,当线程池对象被回收的时候调用,在这里调用shutdown方法终止线程,防止出现内存泄漏
prestartCoreThread 预先启动一个核心线程
prestartAllCoreThreads 预先启动所有的核心线程
remove 从任务队列中移除指定任务
purge 从队列中移除所有的以及取消的Future任务
getPoolSize 获取线程池中线程的数量,即Worker的数量
getActiveCount 获取线程池中正在执行任务的线程Worker数量
getLargestPoolSize 获取线程池曾经开启过的最大的线程数量
getTaskCount 获取总的任务数量,该值为每个Worker以及完成的任务数量,以及正在执行的任务数量和队列中的任务数量
getCompletedTaskCount 获取Worker以及执行的任务数量
beforeExecute 任务执行前调用
afterExecute 任务执行后调用
terminated 线程终止时调用,用来回收资源

3.2 线程池基础结构

线程池的基础结构分为三部分: 阻塞队列BlockingQueue、核心参数和Worker工作线程。

3.2.1 阻塞队列

线程池ThreadLocal是一个阻塞队列BlockingQueue

private final BlockingQueue<Runnable> workQueue;

阻塞队列BlockingQueue主要是用来提供任务缓冲区的功能,工作线程从阻塞队列BlockingQueue中取出任务来执行。

线程池中存放任务用的是offer方法,取出任务用的是poll方法。 阻塞队列BlockingQueue有三种通用策略

直接提交

直接提交,当一个线程提交一个任务的时候,如果没有一个对应的线程来取任务,提交阻塞或者失败。同样的当一个线程取任务的时候,如果没有一个对应的线程来提交任务,取阻塞或者失败。

SynchronousQueue就是这种队列的实现,这种队列通常要求maximumPoolSizes最大线程数量是无界的,避免提交的任务因为offer失败而被拒绝执行。

当提交任务的速率大于任务执行的速率的时候,这种队列会导致线程数量无限的增长。

无界队列

无界队列,无界队列实现的例子是LinkedBlockingQueue,当核心线程都处于忙碌的情况的时候, 提交的任务都会添加到无界队列中,不会有超过核心线程数corePoolSize的线程被创建。

这种队列可能适用于任务之间都是独立的,任务的执行都是互不影响的。

例如,在一个web服务器中,这种队列能够用来使短时间大量的并发请求变得更加平滑,当提交任务的速率大于任务执行的速率的时候,这种队列会导致队列空间无限增长。

有界队列

有界队列,有界队列实现的例子是ArrayBlockingQueue,使用该队列配合设置有限的maximumPoolSizes可以防止资源耗尽,这种情况下协调和控制资源和吞吐量是比较困难的。

队列大小和maximumPoolSize的设置是比较矛盾的:使用容量大的队列和较少的线程资源会减少CPU、OS资源、线程上下文切换等的消耗,但是会降低系统吞吐量。

如果任务频繁的阻塞,例如任务是IO密集的类型,这种情况下系统能够调度更多的线程。使用小容量队列,就要要求maximumPoolSize大一些,从而让CPU保持忙碌的状态,但是可能出现线程上下文切换频繁、线程数量过多调度困难已经创建线程OOM导致资源耗尽的情况,从而降低吞吐量。

SynchronousQueue vs LinkedBlockingQueue vs ArrayBlockingQueue

SynchronousQueue

SynchronousQueue适用于请求响应要求无延迟,请求 并发 量较少的场景

当线程Worker没有从队列取任务的时候,offer返回false,直接开启线程。当Worker从队列取任务的时候,该任务可以直接提交给Worker执行。

因此,这种线程池不会出现等待的情况,响应速度很快。

队列的缺点是提交任务速度大于任务执行速度时,会导致线程无限增长,因此,使用场景需要是并发量较少的情况。

例如,在OkHttp框架中执行HTTP请求就是用的这种队列构建的线程池。

LinkedBlockingQueue

LinkedBlockingQueue适用于并发量高,任务之间都是独立的,互不影响的场景。

比如在web服务器中,面对瞬时大量请求的涌入,可以更加平滑的处理,从而起到削峰的作用,并且防止线程资源的耗尽。

ArrayBlockingQueue

ArrayBlockingQueue是介于前两者之间的队列,可以协调系统资源和吞吐量之间的平衡。

2.3.2.2 核心参数

一个Worker代表一个工作线程,wrokers存储的是线程池中所有的工作线程。

工作线程的核心参数有如下

private final HashSet<Worker> workers = new HashSet<Worker>();

// ---------------------------------------------------------------

// 曾经开启过的最大的线程数量
private int largestPoolSize;
// 完成的任务的数量
private long completedTaskCount;

// ---------------------------------------------------------------

private volatile ThreadFactory threadFactory;
private volatile RejectedExecutionHandler handler;
private volatile long keepAliveTime;
private volatile boolean allowCoreThreadTimeOut;
private volatile int corePoolSize;
private volatile int maximumPoolSize;

这几个变量都是用户设置的参数变量,为了保证参数设置的可见性,所有参数都使用volatile修饰。 ThreadFactory是线程创建工厂,提供线程创建和配置的接口,这里使用的是工厂方法模式,默认的实现是DefaultThreadFactory。

static class DefaultThreadFactory implements ThreadFactory {
   
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    DefaultThreadFactory() {
   
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }
    //注意,Runnable r 就是工作线程接口Worker,需要传到Thread中。
    public Thread newThread(Runnable r) {
   
        Thread t = new Thread(group,r,
                               namePrefix + threadNumber.getAndIncrement()0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

默认的线程工厂创建的线程名称为pool-poolNumber-thread-threadNumber,第一个线程池第一个线程名称为pool-0-thread-0,第二个线程名称为pool-0-thread-1,第二个线程池第一个线程名称为pool-1-thread-0,第二个线程名称为pool-1-thread-1,依次类推。

  • RejectedExecutionHandler
    • 是当任务 被拒绝时的执行接口,提供了4种实现策略,默认采取AbortPolicy策略,如果不设置,线程池在拒绝任务的时候会抛出异常。
  • CallerRunsPolicy
    • 在当前提交线程直接运行该任务。
  • AbortPolicy
    • 直接抛出RejectedExecutionException异常。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值