美文网首页
Android性能优化之线程优化

Android性能优化之线程优化

作者: 怪兽来啦啦啦啦啦 | 来源:发表于2020-04-19 22:14 被阅读0次

    前言

     线程在使用过程中,往往会遇见直接new Thread()的操作,这样做除了方便外,坏处也有很多,例如线程无法复用、线程数无法控制导致CPU频繁切换降低性能等问题。我们应当在项目早期就有意识的使用线程池收敛线程,降低后期的工作量。


    1 线程调度

    1.1 线程调度原理

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

    1.2 线程调度模型

    • 分时调度模型:轮流获取、均分CPU时间
    • 抢占式调度模型:优先级高获取使用权,如果优先级都一样就随机选择线程执行。JVM使用此模型

    1.3 Android线程调度

     android为线程调度分别提供了设置nice值、cgroup两种方法。

    • nice值
      Process中定义,值越小优先级越高。默认优先级是Process.THREAD_PRIORITY_DEFAULT
      Android线程优先级
    • cgroup
      为了避免前、后台线程过多时相互影响对方执行,Android采取更严格的群组调度策略,保证前台线程可以获取到更多的CPU,但是又不会严重影响到后台线程的计算。
    • 什么线程会被分配到后台线程?
      优先级较低的线程、不在前台使用的线程会被移动到后台线程。
    • nice值和cgroup如何使用?
      nice值和cgroup两个值,我们只能设置nice值
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
    

    -注意点
    ①线程过多会导致CPU频繁切换,降低线程运行效率。
    ②任务量大的线程应为较低优先级,避免产生①问题
    ③优先级具有继承性


    2 Android异步

     除了可以使用线程池外,Android还为我们提供了三种异步方法:HandlerThread、IntentService、AsyncTask。

    2.1 Thread

     最简单粗暴的方式,不建议直接使用,因为存在以下几点问题:
    ①不易复用,频繁创建及销毁开销大
    ②复杂场景不易使用,例如定时任务

    2.2 HandlerThread

    • 自带消息循环
      ①串行执行
      ②长时间运行,不断从队列中获取任务

    2.3 IntentService

    • 继承自Service在内部创建HnadlerThread
      ①异步,不占用主线程
      ②优先级较高,不易被系统kill

    2.4 AsyncTask

    • Android提供异步工具类
      ①无需自己处理线程切换
      ②注意版本不一致

    2.5 线程池

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

    2.6 RxJava

    • 由强大的Scheduler集合提供
      ①不同类型的区分:IO、Computation

    3 Android线程优化

    3.1 使用准则

    • 严禁直接new Thread
    • 提供基础线程池供各个业务线使用
      ①避免各个业务线各自维护一套线程池,导致线程数过多
    • 根据任务类型选择合适的异步方式
      ①优先级低,长时间执行可以使用HandlerThread
    • 创建线程必须命名
      ①方便定位线程归属
      ②运行期Thread.currentThread().setName修改名字。因为重命名容易被忽略,需要注意
    • 关键异步任务监控
      ①异步不等于耗时
      ②AOP监控
    • 重视优先级设置
      ①Process.setThreadPriority

    3.2 线程池工具类

     项目走向成熟期之后需要对线程进行收敛,避免线程随意创建,如果集成的框架能主动设置线程池,我们应当设置成自己的线程池,对线程创建及使用情况进行监控。

    public class ThreadPoolUtils {
        private final Executor mDiskExecutor;
    
        private final Executor mNetworkExecutor;
    
        private final Executor mMainThread;
        private final ScheduledThreadPoolExecutor schedule;
        //cpu密集型任务最大线程数
        private int CPU_Runtime = Runtime.getRuntime().availableProcessors() + 1;
        //io密集型任务最大线程数
        private int IO_Runtime = Runtime.getRuntime().availableProcessors() * 2 + 1;
    
        private static ThreadPoolUtils instance;
    
        private static final Object object = new Object();
    
        public static ThreadPoolUtils getInstance() {
            if (instance == null) {
                synchronized (object) {
                    if (instance == null) {
                        instance = new ThreadPoolUtils();
                    }
                }
            }
            return instance;
        }
    
        private ThreadPoolUtils() {
    
            this.mDiskExecutor = Executors.newFixedThreadPool(CPU_Runtime, new MyThreadFactory("MonsterDE"));
    
            this.mNetworkExecutor = Executors.newFixedThreadPool(IO_Runtime, new MyThreadFactory("MonsterNE"));
    
            this.mMainThread = new MainThreadExecutor();
    
            this.schedule = new ScheduledThreadPoolExecutor(IO_Runtime, new MyThreadFactory("MonsterSC"),
                    new ThreadPoolExecutor.AbortPolicy());
        }
    
         /**
         * 记录是线程池中第几个线程
         */
        private static class MyThreadFactory implements ThreadFactory {
    
            private final String name;
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            MyThreadFactory(String name) {
                this.name = name;
            }
    
            @Override
            public Thread newThread(@NonNull Runnable r) {
                return new Thread(r, name + "-" + mCount.getAndIncrement() + "-");
            }
        }
    
        public Executor diskIO() {
            return mDiskExecutor;
        }
    
        public ScheduledThreadPoolExecutor schedule() {
            return schedule;
        }
    
        public Executor networkIO() {
            return mNetworkExecutor;
        }
    
        public Executor mainThread() {
            return mMainThread;
        }
    
        private static class MainThreadExecutor implements Executor {
            private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
    
            @Override
            public void execute(@NonNull Runnable command) {
                mainThreadHandler.post(command);
            }
        }
    }
    
    

    4 锁定线程

    • 为什么要锁定线程?
      项目变大之后需要收敛线程,因为我们自身的项目源码、第三方库、aar文件都有可能存在线程的创建,我们需要通过监控预防去预防项目往不好的地方发展。
    • 锁定线程创建方案
      因为AAR可能混淆过,无法通过在new Thread中使用,但是我们可以在线下使用ARTHook方法,在线程构造函数打印调用堆栈。

    总结

     线程优化可以分为以下几点:

    • 修改线程优先级(任务量大的可以降低优先级;但是一般不建议修改优先级,因为都想着自己线程的优先级更高的时候,线程优先级设置会变得无意义,深层次的原因是映射到系统线程等级问题,这里就不再展开说明)
    • 选择合适的异步方法,例如Android提供的三种异步方法以及线程池,开发过程中可根据实际情况选择
    • 使用线程池进行线程收敛,避免直接new Thread()
    • 锁定线程,可通过ARTHook方案对线程进行监控,并打印线程堆栈信息。

    相关文章

      网友评论

          本文标题:Android性能优化之线程优化

          本文链接:https://www.haomeiwen.com/subject/yefiphtx.html