App 线程优化的各种方案

作为性能优化的第六篇,我们就来说一说 线程优化。



零、前言

对于线程优化,可能有的人说就是把 new Thread 改成线程池,这么说可能有点差强人意。那么本篇文章可能会稍微开拓一下视野。



一、Android 线程调度机制


1.1 线程调度原理
  • 任意时刻,只有一个线程占用 CPU,处于运行状态。
  • 多线程并发:轮流获取 CPU 使用权。
  • JVM 负责线程调度:按照特定机制分配 CPU 使用权。

1.2 线程调度机制
  • 分时调度模型:所有的线程轮流获取 CPU 使用权,平均分配每个线程占用时间。
  • 抢占式调度模型:优先让可运行池中的优先级高的线程占用 CPU,优先级相同随机选择一个线程,JVM 采用。

在 Android 中采用的是 抢占式调度模型

可以设置线程的优先级,具体做法是通过 android.os.Process.setThreadPriority(int) 设置线程优先级,参数范围是 -20 到 24 ,数值越小优先级越高,0 位默认的优先级。默认情况下,新创建的线程的优先级默认与母线程一致。

Android 系统会根据当前运行的可见程序和不可见程序对线程进行归类。保证前台线程获取更多的 CPU 时间。



二、Android 中的异步方式


2.1 Thread

最简单,最常见的异步方式,直接创建一个线程。不宜服用,频繁创建及销毁开销大,复杂场景不易使用。


2.2 AsyncTask

为 UI 线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。

默认是线性调度执行,会阻塞后续任务,可以指定线程池并发调度。


2.2 HandlerThread

为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。它是串行执行,适合长时间运行,不断从队列中获取任务的场景。


2.3 IntentService

继承自 Service 在内部创建 HandlerThread ,在 onHandlerIntent 的回调里面处理任务,不受主页面生命周期影响,异步且不占用主线程,优先级较高,不易被系统 kill。适合执行由 UI 触发的后台 Service 任务,并可以把后台任务执行的情况通过一定的机制反馈给 UI。


2.4 ThreadPool

把任务分解成不同的单元,分发给各个不同的线程上,进行同时并发处理。

易复用,减少频繁创建,销毁的时间,功能强大:定时、任务队列、并发数控制等。



三、线程使用准则

  • 严禁直接 new Thread。
  • 提供基础线程池供各个业务线使用,避免各个业务线各自维护一套线程池,导致线程数过多。
  • 根据任务类型选择合适的异步方式,例如优先级低,长时间执行的使用 HandlerThread。
  • 创建线程必须命名,目的是方便定位线程归属,运行可以修改线程名字,结束后再改回。
  • 重视优先级设置,使用 Process.setThreadPriority()


四、锁定线程


4.1 背景
  • 项目变大之后收敛线程。
  • 项目源码、三方库、aar中都有线程创建。
  • 避免恶化的一种监控预防手段。

4.2 方法

在创建线程的位置获取堆栈信息,所有的异步方式,都会走到 new Thread 方法中。特别适合 Hook 手段,找到 Hook 点:构造函数或者特定方法,也就是 Thread 的构造函数。



五、线程池模板代码

public class ThreadPoolUtils {

    private int CPUCOUNT = Runtime.getRuntime().availableProcessors();

    private ThreadPoolExecutor cpuExecutor = new ThreadPoolExecutor(CPUCOUNT, CPUCOUNT,
            30, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(), sThreadFactory);

    private ThreadPoolExecutor iOExecutor = new ThreadPoolExecutor(64, 64,
            30, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(), sThreadFactory);

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "ThreadPoolUtils #" + mCount.getAndIncrement());
        }
    };

    public static ExecutorService getService() {
        return sService;
    }

    private static ExecutorService sService = Executors.newFixedThreadPool(5, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "ThreadPoolUtils");
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return thread;
        }
    });
}


//使用
 ThreadPoolUtils.getService().execute(new Runnable() {
            @Override
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                String oldName = Thread.currentThread().getName();
                Thread.currentThread().setName("new Name");
                LogUtils.i("");
                Thread.currentThread().setName(oldName);
            }
        });




写在文末

纸上得来终觉浅,绝知此事要躬行。 《冬夜读书示子聿》-- 陆游

好了,关于 App 线程优化实战 就介绍到这,希望本篇文章能对您有所帮助。


码字不易,如果本篇文章对您哪怕有一点点帮助,请不要吝啬您的点赞,我将持续带来更多优质文章。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值