Android性能优化——线程优化

一、线程调度原理

在任意时刻,CPU只能执行一条指令,每个线程获取到CPU的使用权之后才可以执行指令也就是说在任意时刻,只有一个线程占用CPU 处于运行状态
多线程并发,实际上是指多个线程轮流获取CPU 的使用权然后分别执行各自的任务,在可运行区中,其实有多个线程处于就绪状态的线程在等待CPU 
而JVM 的一项任务,就是要负责线程的调度线程的调度就是按照特定的机制为多个线程来分配CPU的使用权,

二、线程调度模型

  • 分时调度模型:它的思想是让线程轮流获取CPU 的使用权,并且平均每个线程占用CPU 的时间片
  • 抢占式调度模型:Java虚拟机采用的模型,它的思想是优先让可运行池中优先级高的线程来占用CPU ,如果运行池中的优先级都一样,那就随机选择一个。如果程序想要干预运行顺序,那就给每个线程设置一个优先级

三、Android线程调度

由两个因素来决定:

  • nice值

     在Process 中定义
     值越小,优先级越高
     默认值是:THREAD_PRIORITY_DEFAULT,0
     最低是19,后台优先级是16
     可以设置成负数,优先级更高
     
如果后台进程比较多,会影响前台进程的运行,所以还需要另一种机制来处理这种特殊的情况

  • cgroup 

借助Linux 的cgroup来执行更严格的前台和后台策略,后台线程会被隐式的移到后台group ,当其他组的线程处于工作状态,那后台group 的线程就会被限制,使用很小的几率来利用CPU ,这种分离的调度策略既允许了后台线程来执行一些任务,同时不会对用户的前台线程造成影响,保证前台线程能使用更多的CPU 
哪些线程会被移到后台group?

  • 优先级设置的比较低的线程
  • 不在前台运行的应用程序的线程

注意点

  • 线程过多会导致CPU 频繁切换,降低线程运行效率异步不能无限制的使用
  • 正确认识到线程执行任务的重要性,来决定哪种优先级,一般优先级与线程的工作量成反比
  • 线程的优先级具有继承性,比如在UI线程中直接创建一个子线程,它的优先级和UI 线程一样高

四、异步方式汇总

Thread 

  •  最简单,常见的异步方式
  • 不易复用,频繁创建及销毁开销大
  • 复杂场景不易使用,比如要执行一个定时任务thread方式不方便使用

HandlerThread 

  •  本质是一个Thread ,自带消息循环
  •  内部串行执行任务
  • 比较适合那些需要长时间运行,不断的从队列中获取任务的场景

IntentService 

  • 内部实现:继承自service 在内部创建HandlerThread ,继承了HandlerThread 的特性
  • 相对于service 来说,它的执行是异步的,不会阻塞UI线程的执行
  •  优先级较高,不会被系统kill 

AsyncTask 

  • Android提供的异步工具类
  • 内部实现是两个线程池,一个handler
  • 无需开发者去处理线程切换的问题
  • 需注意版本不一致的问题,实现方式不一致,适配Android版本要在14以上

线程池

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

RxJava 

  • 由强大的Scheduler 集合提供
  • 不同的类型区分,IO,Comptution 
  • 如果项目中集成了RxJava 推荐使用RxJava的线程池

五、线程使用准则

  • 严禁直接new Thread 
  • 提供基础线程池供各个业务线使用,避免各个业务线各自维护一套线程池,导致线程数过多
  • 根据任务类型选择合适的异步方式

    优先级低,长时间执行,使用HandlerThread
    定时执行,使用线程池

  • 创建线程必须命名

  方便定位线程归属
  运行期,通过Thread.currentThread().setName()修改名字

  • 对关键异步任务进行监控

    异步不等于不耗时
    AOP的方式来做监控

  • 重视优先级设置,Java的线程调度是一个抢占式的调度模型

   Process.setThreadPriority()
   可以设置多次

六、锁定线程创建

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

解决方案
   分析:

  • 创建线程的位置去获取堆栈信息
  • 所有的异步方式,都会走到new Thread 
  • 由于有的源码拿不到,不能直接在new Thread 的地方直接去获取堆栈信息,此时特别适合采用Hook手段,在特定的方法之内注入自己的逻辑

    找Hook 点,构造函数或者特定方法
    在这里,就可以找Thread 的构造函数在构造方法里加入自己的逻辑,获取调用栈信息拿到调用栈信息就能知道哪个调用不是调用我们自己的线程也可以知道调用栈信息是属于哪一个业务方

DexposedBridge.hookAllConstructors(Thread. class, new XC_MethodHook(
@Overrideprotected void afterHookedMethod (MethodHookParam param) throw Throwable {super.afterHookedMethod (param);Thread thread = (Thread) param.thisObject;LogUtils.i ( msg: thread. getName ()+" stack "+Log.getStackTraceString(new throwable);

七、线程收敛

 常规方案:

  •  根据线程创建堆栈考量合理性,使用统一线程库
  •  各个业务线需要下掉自己的线程库使用统一的线程库

基础库怎么使用线程

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

优雅的实现

  • 基础库内部暴露API setEeecutor 基础库只需要修改一次
  • 初始化的时候注入统一的线程库

统一线程库

  • 区分任务类型:IO、CPU密集型
  • IO密集型任务不消耗CPU,核心池可以很大
  • CPU密集型任务:核心池大小和CPU核心数相关

八、模拟问题

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

  • 在项目的初期阶段,主要是关注业务功能,忽视了基础库的建设,具体到线程方面,没有采用统一的线程池,每个地方使用线程的方式比较乱,同时线程数量比较多
  • 在项目发展壮大之后,遇到了一些线程的性能问题,比如说主线程卡顿,以及一些异步任务执行非常耗时
  • Java的线程调度是一个抢占式的调度模型线程优先级比较重要,但是项目中并没有做这些区分,对IO和CPU密集型的任务也没有做区分,很有可能主线程抢不到时间片的情况

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

  • 首先针对于项目中线程数过多的这种情况,做了线程收敛,通过hook 手段来获取每个线程运行的堆栈信息
  • 然后结合业务场景来看这个线程是否需要单独来创建通过这种方式,尽可能在业务层面将线程收敛到统一的线程库当中,而对于基础库,每个基础库统一对外暴露一个接口,提供一个线程池实现的能力,在基础库使用之前来注入线程库,这样基础库都用到了线程库
  • 基础线程库针对于IO密集型任务和CPU密集型任务做区分,对于IO密集型任务,比如网络请求,文件操作,它并不消耗CPU ,所以将核心池设置的比较大,而对于CPU密集型任务,如果核心数数量过高,他可能会导致CPU 频繁调度,反而会导致执行效率下降,因此根据CPU核心数来决定CPU线程池核心数大小
  •  还做了其他处理,比如对重要的异步逻辑进行监控,监控他的执行时间,同时在执行异步任务的时候注重优先级以及线程名的设置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/41477.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统安全测试要怎么做?

进行系统安全测试时,可以按照以下详细的步骤进行: 1、信息收集和分析: 收集系统的相关信息,包括架构、部署环境、使用的框架和技术等。 分析系统的安全需求、威胁模型和安全策略等文档。 2、威胁建模和风险评估: 使…

调用被fishhook的原函数

OC类如果通过runtime被hook了,可以通过逆序遍历方法列表的方式调用原方法。 那系统库的C函数被fish hook了该怎么办呢? 原理和OC类异曲同工,即通过系统函数dlopen()获取动态库,以动态库为参数通过系统函数dlsym()即可获取目标系统…

leetcode292. Nim 游戏(博弈论 - java)

Nim 游戏 Nim 游戏题目描述博弈论 上期经典算法 Nim 游戏 难度 - 简单 原题链接 - Nim游戏 题目描述 你和你的朋友,两个人一起玩 Nim 游戏: 桌子上有一堆石头。 你们轮流进行自己的回合, 你作为先手 。 每一回合,轮到的人拿掉 1 -…

494. 目标和

494. 目标和 原题链接:完成情况:解题思路:数组回溯法动态规划 参考代码:数组回溯法__494目标和__动态规划 经验吸取 原题链接: 494. 目标和 https://leetcode.cn/problems/target-sum/description/ 完成情况&#…

Android进阶之多级列表

遇到一个需求需要显示多级列表,因为界面是在平板上的,所以层级是从左向右往下排的,类似于 我当时的写法是在xml布局里一个个RecyclerView往下排的 当然前提是已经规定好最大的层级我才敢如此去写界面,如果已经明确规定只有两级或…

69 # 强制缓存的配置

强制缓存 强制缓存:以后的请求都不需要访问服务器,状态码为 200协商缓存:每次都判断一下,告诉是否需要找缓存,状态码为 304 默认强制缓存,不缓存首页(如果已经断网,那这个页面应该…

Python发送QQ邮件

使用Python的smtplib可以发送QQ邮件,代码如下 #!/usr/bin/python3 import smtplib from email.mime.text import MIMEText from email.header import Headersender 111qq.com # 发送邮箱 receivers [222qq.com] # 接收邮箱 auth_code "abc" # 授权…

Dockerfile概念、镜像原理、制作及案例讲解

1.Docker镜像原理 Linux文件操作系统讲解 2.镜像如何制作 3.Dockerfile概念 Docker网址:https://hub.docker.com 3.1 Dockerfile关键字 4.案例

【数据结构OJ题】链表分割

原题链接:https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId8&&tqId11004&rp2&ru/activity/oj&qru/ta/cracking-the-coding-interview/question-ranking 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2…

AMD卡启动Stable Diffusion AI绘画的方法

WindowsAMD安装法 1.安装python 3.10.6,在python官网上下载安装程序,***重要*** 在安装的第一个窗口下方勾选“将python添加到path”。 2.安装git 3.WindowsAMD使用AUTOMATIC1111的directml这一个fork,在这个页面的第一段:https:/…

题目:2614.对角线上的质数

​​题目来源: leetcode题目,网址:2614. 对角线上的质数 - 力扣(LeetCode) 解题思路: 遍历对角线上的元素,返回最大的质数或 0 即可。 解题代码: class Solution {public int dia…

e.target.value和 binding.value 区别

e.target.value 和 binding.value 都是在 Vue.js 中用于处理事件绑定时的值,但它们的使用场景和含义有所不同,分别用于普通的 DOM 事件和自定义指令。 e.target.value: 这是常用于原生 DOM 事件处理函数中的一个属性,用于获取事件…

爬虫逆向实战(十七)--某某丁简历登录

一、数据接口分析 主页地址:某某丁简历 1、抓包 通过抓包可以发现数据接口是submit 2、判断是否有加密参数 请求参数是否加密? 通过查看“载荷”模块可以发现有一个enPassword加密参数 请求头是否加密? 通过查看请求头可以发现有一个To…

【面试高频题】难度 3/5,字典树热门运用题

题目描述 这是 LeetCode 上的 「745. 前缀和后缀搜索」 ,难度为 「困难」。 Tag : 「字典树」 设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。 实现 WordFilter 类: WordFilter(string[] words) 使用词典中的单词 words 初…

单片机之从C语言基础到专家编程 - 4 C语言基础 - 4.9 变量与常量

基本数据类型可以作为变量与常量使用,顾名思义,变量运行时可以改变其值,常量运行时不会改变其值。 常量分为整型常量、浮点型常量、字符常量、字符串常量和符号常量。 通常用#define来定义一个标识符来表示一个常量 用type name 常量来定义一个变量,…

无法将“环境变量”项识别为 cmdlet、函数、脚本文件或可运行程序的名称(pycharm)

无法将“配置的任何一个环境变量”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 记录解决“无法将“C:......conda.exe”项识别为 cmdlet、函数、脚本文件或可运行程序的名称”以及“表达式或语句中包含意外的标记”的系列问题(VSCode开发环境)一、Conda.exe无法正常识…

ROS2 学习(三)话题

话题 节点之间的通信。 叫话题很形象。发布者发布一定数据类型的话题,订阅者订阅发布者。 订阅者发布者不唯一。 异步通信,适用于周期发布的数据而不是逻辑性强的数据。 .msg 格式的消息结构,一种通信接口。 每个话题 topic 有话题名&a…

【Java高级开发高频面试题】面试者角度的口述版

文章目录 1.具备扎实的Java基础集合HashMap底层工作原理HashMap版本问题HashMap并发修改异常HashMap影响HashMap性能的因素HashMap使用优化 SynchronizedThreadLocalAQS线程池JVM内存模型类加载机制与双亲委派垃圾回收算法、垃圾回收器、空间分配担保策略引用计数器算法、可达性…

创建 Web 内容目录

创建 Web 内容目录 按照下方所述,创建一个名为 /home/curtis/ansible/webcontent.yml 的 playbook : 该 playbook 在 dev 主机组中的受管节点上运行 创建符合下列要求的目录 /webdev : 所有者为 webdev 组 具有常规权限:ownerread…

Nginx反向代理

目录 一.简介1.反向代理 二.案例1.案例12.案例2 一.简介 1.反向代理 1.1反向代理: 是指代理服务器来接收Internet上的客户端请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给客户端。此时代理服务器对外就表现为一…