作为性能优化的第六篇,我们就来说一说 线程优化。
零、前言
对于线程优化,可能有的人说就是把 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 线程优化实战
就介绍到这,希望本篇文章能对您有所帮助。