6.线程优化

一、线程调度原理分析

线程调度原理

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

线程调度模型

1
2
1.分时调度模型:轮流获取、均分CPU时间
2.抢占式调度模型:优先级高的获取,JVM采用

Android线程调度

1
2
3
4
5
6
7
1.nice值
- Process中定义
- 值越小,优先级越高
- 默认是THREAD_PRIORITY_DEFAULT,0
2.cgroup
- 更严格的群组调度策略(前台/后台)
- 保证前台线程可以获取到更多的CPU

注意点

1
2
3
1.线程过多会导致CPU频繁切换,降低线程运行效率
2.正确认识任务重要性决定哪种优先级(工作量越低,优先级应该越低)
3.优先级具有继承性(A线程中启动线程B,则B基础A的优先级)

二、android异步方式汇总

Thread

1
2
3
最简单、常见的异步方式
- 不易复用,频繁创建及销毁开销大
- 复杂场景不易使用

HandlerThread

1
2
3
自带消息循环的线程
- 串行执行
- 长时间运行,不断从队列中获取任务

IntentService

1
2
3
继承自Service在内部创建HandlerThread
- 异步,不占用主线程
- 优先级较高,不易被系统Kill

AsyncTask

1
2
3
Android提供的工具类
- 无需自己处理线程切换
- 需注意版本不一致问题(14以下存在,目前几乎不用管)

线程池

1
2
3
Java提供的线程池
- 易复用,减少频繁创建、销毁的时间
- 功能强大:定时、任务队列、并发数控制等

RxJava

1
2
由强大的 Scheduler 集合提供
- 不同类型的区分 :IO、Computation

异步方式总结

1
2
推荐度:从前往后排列
正确场景选择正确的方式

三、线程优化实战

线程使用准则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.严禁直接 new Thread
2.提供基础线程池供各个业务线使用
- 避免各个业务线各自维护一套线程池,导致线程数过多
3.根据任务类型选择合适的异步方式
- 优先级低,长时间执行,HandlerThread
4.创建线程必须命名
- 方便定位线程归属
- 运行期Thread.currentThread0.setName修改名字
5.关键异步任务监控
- 异步不等于不耗时
- 可通过AOP的方式来做监控
6.重视优先级设置
- Process.setThreadPriority();
- 可以设置多次;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//ThreadPoolUtils工具类
package com.optimize.performance.async;


import android.os.Process;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

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;
}
});
}
1
2
3
4
5
6
7
8
9
10
11
//使用示例
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);
}
});

四、如何锁定线程创建者

锁定线程创建背景

1
2
3
1.项目变大之后收敛线程
2.项目源码、三方库、aar中都有线程的创建
3.避免恶化的一种监控预防手段

锁定线程创建方案

1
2
3
4
5
6
1.分析
- 创建线程的位置获取堆栈
- 所有的异步方式,都会走到new Thread
2.特别适合Hook手段
3.找Hook点:构造函数或者特定方法
4.Thread的构造函数
1
2
3
4
5
6
7
8
DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread thread = (Thread) param.thisObject;
LogUtils.i(thread.getName()+" stack "+Log.getStackTraceString(new Throwable()));
}
});

五、线程收敛优雅实现

线程收敛常规方案

1
2
1.根据线程创建堆栈考量合理性,使用统一线程库
2.各业务线下掉自己的线程库

基础库怎么使用线程

1
2
1.直接依赖线程库
2.缺点:线程库更新可能会导致基础库更新

基础库优雅使用线程

1
2
1.基础库内部暴露API:setExecutor
2.初始化的时候注入统一的线程库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.optimize.performance.utils;

import android.util.Log;
import com.optimize.performance.PerformanceApp;
import java.util.concurrent.ExecutorService;

public class LogUtils {
private static ExecutorService sExecutorService;
public static void setExecutor(ExecutorService executorService){
sExecutorService = executorService;
}
public static final String TAG = "performance";
public static void i(String msg){
if(Utils.isMainProcess(PerformanceApp.getApplication())){
Log.i(TAG,msg);
}
// 异步
if(sExecutorService != null){
// sExecutorService.execute();
}
}
}

统一线程库

1
2
3
1.区分任务类型:IO、CPU密集型
2.IO密集型任务不消耗CPU,核心池可以很大
3.CPU密集型任务:核心池大小和CPU核心数相关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.optimize.performance.async;
import android.os.Process;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

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;
}
});
}

六、问题

线程使用为什么会遇到问题

1
2
1.项目发展阶段
2.问题原因及表现形式

怎么在项目中对线程进行优化

1
2
3
1.线程收敛
2.统一线程池:任务区分
3.其他细节