Android并发编程与多线程
一、Android线程基础
1.线程和进程
- 一个进程最少一个线程,进程可以包含多个线程
 - 进程在执行过程中拥有独立的内存空间,而线程运行在进程内
 
2.线程的创建方式
- new Thread:
 
缺点:缺乏统一管理,可能无限制创建线程,相互之间竞争,可能占用过多的系统资源导致死机或OOM
    new Thread(new Runnable() {@Overridepublic void run() {}}).start();class MyThread extends Thread{@Overridepublic void run() {super.run();}}new MyThread().start(); 
- AysncTask:轻量级异步任务工具类,提供任务执行的进度回调给UI线程
 
使用场景:需要知道任务执行的进度,多个任务串行执行
缺点:生命周期和宿主的生命周期不同步,可能发生内存泄漏,默认情况下任务串行执行
作为用来替代Thread + Handler的辅助类,AsyncTask可以很轻松地执行异步任务并更新ui,但由于context泄露,回调遗漏,configuration变化导致崩溃,平台差异性等原因,在api 30(Android 11)中AsyncTask被正式废弃:
使用execute方法,串行执行,即先来后到,如果其中有一条任务休眠,或者执行时间过长,后面的任务都将被阻塞
使用内置THREAD_POOL_EXECUTOR线程池,并发执行
- HandlerThread:适用于主线程需要和工作线程通信,或者持续性任务,比如轮询,所有任务串行执行
 
缺点:不会像普通线程一样主动销毁资源,会一直运行,可能造成内存泄漏(可以定义成静态,防止内存泄漏)
- IntentService:适用于任务需要跨界面读取任务执行的进、结果。比如:后台上传图片、批量操作数据库等。任务完成后,会自我结束,不需要手动stopService
 - ThreadPoolExecutor:适用快读出来大量耗时较短的任务场景 
- Executors.newCacheThreadPool();//线程可复用线程池
 - Executors.newFixedThreadPool();//固定线程数量线程池
 - Executors.newScheduleThreadPool();//可指定定时任务线程池
 - Executors.newSingleExecutor();//线程数量为1的线程池
 
 
3.线程的优先级
- 线程的优先级具有继承性
 - 设置优先级的方法 
- java api:java.lang.Thread.setPriority(int newPriority);优先级必须为[1~10],优先级的值越高,获取CPU时间片的概率越高,UI线程的优先级为5 效果不是很明显
 - Android api:android.os.Process.setThreadPriority(int newPriority);优先级可设置[-20~19],优先级的值越低,获取时间片的概率越高,UI线程的优先级为-10 效果明显
 
 
4.线程的状态
- NEW:初始状态,线程被新建,还没有调用start方法
 - RUNNABLE:运行状态,包括运行中和就绪
 - BLOCKED:阻塞状态,表示线程阻塞于锁
 - WAITING:等待状态,需要其他线程通知唤醒
 - TIME_WAITING:超时等待状态,表示可以在指定的时间超过后自行返回
 - TERMINATED:终止状态,表示当前线程已经执行完毕
 
关键方法:
- wait:等待线程池,释放资源对象锁,可使用notify,notifyAll,或等待超时时间来唤醒
 - join:等待目标线程执行完后再执行此线程
 - yield:暂停当前正在执行的线程对象,不会释放当前线程持有的任何锁资源,使用优先级或更高优先级的线程有执行的机会,这个方法机会用不到
 - sleep:使调用线程进入休眠状态,一般情况下会释放线程锁对象,但如果在一个synchronized块中执行sleep,线程虽会休眠,但不会释放资源对象锁
 
5.线程间消息通讯
主线程向子线程发送消息
二、多线程开发核心知识点
1.线程并发安全
本质:能够让并发线程有序的运行(可能是先来后到排队,也可能被插队,同一时刻只能一个线程有权访问同步资源),线程执行的结果,能够对其它线程可见
2.线程安全的分类
- 根据线程要不要锁住同步资源 
- 锁住:悲观锁 synchronized ReentrantLock
 - 不锁住:乐观锁 AtomicInteger AtomicBoolean
 
 - 锁住同步资源失败要不要阻塞? 
- 阻塞:阻塞锁 synchronized ReentrantLock
 - 不阻塞:自旋锁 AtomicInteger AtomicBoolean
 
 - 获取资源锁时,要不要插队 
- 插队:公平锁 ReentrantLock
 - 不插队:非公平锁 synchronized ReentrantLock
 
 - 一个线程中的多个流程能不能获取同一把锁 
- 可重入锁: synchronized ReentrantLock
 - 不可重入锁
 
 - 多线程共享一把锁 
- 能:共享锁 RendLock
 - 不能:排他锁 WriteLock
 
 - 多线程竞争同步资源 synchronized 
- 不锁住资源,多个线程中只有一个能修改成功,其他会重试 无锁
 - 同一线程执行同步资源时自动获取锁 偏向锁
 - 多个线程竞争资源时,没有获取到资源的线程自选等待 轻量级锁
 - 多个线程竞争资源时,没有获取到资源的线程被阻塞等待唤醒 重量级锁
 
 
3.如何线程安全
AtomicInterger 原子包装类
- CAS实现无锁数据更新,自旋的设计能够有效避免线程因阻塞-唤醒带来的系统资源开销
 - 适用场景:多线程计数,原子操作,并发数量小
 - 使用案例
 
        AtomicInteger atomicInteger = new AtomicInteger(1);atomicInteger.getAndIncrement();atomicInteger.getAndAdd(2);atomicInteger.getAndDecrement();atomicInteger.getAndAdd(-2); 
volatile 可见性修饰
- volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存
 - 缺点:不能保证原子性,不能解决非原子操作的线程安全性,性能不及原子类高
 - 使用案例
 
    volatile int count = 0;public void test() {// 赋值操作是原子性操作,对其他线程可见count = 1;//非原子操作,其他线程不可见count = count + 1;count++;} 
synchronized
- 锁方法。加在方法上,未获取到对象锁的其他线程都不可以访问该对象
 - 锁Class对象。加在static方法上相当于给Class对象加锁,哪怕是不同的Java对象实例,也需要排队执行
 - 锁代码块。未获取到对象锁的其他线程可以执行同步代码块之外的代码
 
优势:哪怕一个同步方法中出现了异常,那么JVM也能够为我们自动释放锁,能主动从而规避死锁,不需要开发者主动释放锁
劣势:
- 必须要等到获取锁对象的线程执行完成,或出现异常,才能释放掉,不能中途释放锁,不能中断一个正在试图获取锁的线程
 - 多线程竞争的时候,不知道获取锁成功与否,不够灵活
 - 每个锁仅有单一的条件不能设定超时
 
ReentrantLock
悲观锁,可重入锁,公平锁,非公平锁
- 基础用法:
 
        ReentrantLock lock = new ReentrantLock();try {lock.lock();//……} finally {lock.unlock();}//获取锁,获取不到会阻塞lock.lock();//尝试获取锁,成功返回truelock.tryLock();//在一定的时间内去不断尝试获取锁lock.tryLock(3000, TimeUnit.MICROSECONDS);//可使用Thread.interrupt()打断阻塞,退出竞争,让给其他线程lock.lockInterruptibly(); 
- 可重入,避免死锁
 
    ReentrantLock lock = new ReentrantLock();public void doWork() {try {lock.lock();doWork();//递归调用,使得统一线程多次获得锁} finally {lock.unlock();}} 
- 公平锁:所有进入阻塞的线程排队依次均有机会执行
 
使用场景:交易
//传入true 就是公平锁,传入false 或者不传就是非公平锁 
ReentrantLock lock = new ReentrantLock(true); 
- 非公平锁:默认,允许线程插队,避免每一个线程都进入阻塞,在唤醒带来的性能开销。性能高,因为线程可以插队,但是会导致队列中可能存在线程饿死的情况,一直得不到锁,一直得不到执行
 
使用场景:synchronized,很多场景都是非公平锁
- 进阶用法 -- Condition条件对象
 
  Condition worker = lock.newCondition();//进入阻塞,等待唤醒worker.await();//唤醒指定线程worker1.signal(); 
共享锁,排他锁
- 共享锁:所有线程都可以同时获得,并发量高。如:读操作
 - 排他锁:同一时刻只能一个线程有权修改资源。如:写操作
 
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock.WriteLock 
三、正确使用锁和原子类
1.减少持锁时间
尽管锁在同一时间只能允许一个线程持有,其他想要占用锁的线程都得在临界区外等待锁的释放,这个等待的时间要尽可能的短
2.锁分离
读写锁分离,写锁才需要同步处理。对于大多数应用来说,读的场景更多一些,读写锁分离,可以提高系统性能
3.锁粗化
多次加锁,释放锁合并成一次
对于一些不需要同步的代码,但能很快执行完毕,前后都有锁,这种情况可以进行锁粗化,整合成一次锁请求,释放。(锁请求、释放是需要性能开销的)
四、线程池
1.优势
- 减低资源消耗。通过重复利用已创建的线程减低线程创建和销毁造成的消耗;
 - 提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行;
 - 提高线程的可管理性。线程是稀有资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
 
2.Java中默认的线程池
线程池的默认构建
       ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 5, TimeUnit.SECONDS,new PriorityBlockingQueue<>());executor.execute(() -> {});// 源码public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {} 
参数
- corePoolSize:线程池中的核心线程数量
 - maximumPoolSize:最大能创建的线程数量
 - keepAliveTime:非核心线程最大的存活时间
 - unit:keepAliveTime的时间单位
 - workQueue:等待队列,当任务提交时,如果线程中线程数量大于等于corePoolSize的时候,把任务放入等待队列
 - threadFactory:线程创建工厂,默认使用Executors.defaultThreadFactory()来创建线程,线程具有相同的NORM_PRIORITY优先级并且是非守护线程
 - handler:线程池的饱和就决策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务
 
JUC包下提供的集中线程池
        // 单一线程数,同时只有一个线程存活,但是线程等待队列无界Executors.newSingleThreadExecutor();// 线程可复用线程池,核心线程数为0,最大可创建的线程数为Interger.max,线程复用存活时间为60sExecutors.newCachedThreadPool();// 固定线程数量的线程池Executors.newFixedThreadPool(5);// 可执行定时任务,延迟任务的线程池Executors.newScheduledThreadPool(5); 
线程池的重要方法
//提交任务,交给线程池调度
void execute(Runnable command) //关闭线程池,等待执行任务完成,不接受新的任务,但可以继续执行池子中已添加到等待队列的任务
void shutdown()//关闭线程池,不等待执行任务完成,不接受新的任务,也不再处理等待队列中的任务打断正在执行的任务
void shutdownNow()//返回线程池中所有任务的数据
long getTaskCount()//返回线程池中已执行完毕的任务数量
long getCompletedTaskCount() //返回线程池中已创建的线程数量
int getPoolSize()//返回当前正在运行的线程数量
int getActiveCount() 
execute提交任务流程

addWorker的工作任务
- 检查线程池状态,能否继续创建线程
 - 把runnable封装成worker,添加到工作队列
 - 启动新建的线程
 - runWorker方法中开启whille循环,执行本次任务,本次任务结束后,去检查等待队列中,是否有任务,拿来继续执行,达到复用目的
 
retry:双层for循环流程控制,使用retry可以退出外层循环
        int count = 0;retry:for (int i = 0; i < 10; i++) {for (int j = 0; j < 10; j++) {count++;if (count == 3) {break;}if (count == 4){break retry;}}} 
五、协程
相关文章:
Android并发编程与多线程
一、Android线程基础 1.线程和进程 一个进程最少一个线程,进程可以包含多个线程进程在执行过程中拥有独立的内存空间,而线程运行在进程内 2.线程的创建方式 new Thread: 缺点:缺乏统一管理,可能无限制创建线程&…...
ChatGPT简介及基本概念
点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列点击跳转>ChatGPT和AIGC 👉关于作者 专…...
学习模拟简明教程【Learning to simulate】
深度神经网络是一项令人惊叹的技术。 有了足够的标记数据,他们可以学习为图像和声音等高维输入生成非常准确的分类器。 近年来,机器学习社区已经能够成功解决诸如对象分类、图像中对象检测和图像分割等问题。 上述声明中的加黑字体警告是有足够的标记数…...
电子学会C/C++编程等级考试2021年12月(一级)真题解析
C/C++等级考试(1~8级)全部真题・点这里 第1题:输出整数部分 输入一个双精度浮点数f, 输出其整数部分。 时间限制:1000 内存限制:65536输入 一个双精度浮点数f(0 < f < 100000000)。输出 一个整数,表示浮点数的整数部分。样例输入 3.8889样例输出 3 答案: //参…...
数字游戏
题目描述 小 K 同学向小 P 同学发送了一个长度为 8 的 01 字符串 来玩数字游戏,小 P 同学想要知道字符串中究竟有多少个 1。 注意:01 字符串为每一个字符是 0 或者 1 的字符串,如“101”(不含双引号)为一个长度为 3 …...
k8s pod 处于Terminating的原因分析和解决处理——筑梦之路
之前整理了一下各种资源长时间无法回收,解决处理的命令行 k8s 各种资源Terminationg状态处理 —— 筑梦之路_k8s自定义资源修改状态-CSDN博客 这里具体整理下pod长时间处于Terminating状态的相关知识,主要是对前面的补充和完善,作为笔记记录…...
西南科技大学814考研二
C语言数据结构与算法 线性表 顺序表(静态分配内存) #include <stdio.h> #include <stdbool.h> //静态顺序表 #define MAX_SIZE 8 //顺序表储存的数据类型 typedef int ElemType; typedef struct {ElemType data[MAX_SIZE];int length; }SeqList; //初始化顺序表…...
oracle21c报错 【ORA-65096: 公用用户名或角色名无效】
1.数据库版本 oracle21c 2.问题提示 创建用户提示【ORA-65096: 公用用户名或角色名无效】 create user 自定义用户名 identified by 密码;--例:用户为test1,密码为123456 create user test1 identified by 123456;三.解决办法及结果 oracle11g之后的版本ÿ…...
C++ 递增/递减运算符重载
作用: 通过重载递增运算符,实现自己的整型数据 总结: 前置递增返回引用,后置递增返回值 递增 #include<iostream> using namespace std;class MyInteger { private:int m_Num 0; public:friend ostream& operator<…...
Android 13.0 无源码app增加授予相关权限
1.概述 在13.0的系统rom产品定制化开发中,对于一些无源码app增加一些权限,比如悬浮窗权限,由于app内部没申请这个权限, 所以需要系统适配默认授予这个权限,就需要在PMS解析安装app的时候 授予悬浮窗权限就可以了 2.无源码app增加授予相关权限的核心类 frameworks/base/cor…...
CI/CD相关概念学习
文章目录 CI/CD相关概念学习前言CI/CD相关概念介绍集成地狱持续集成持续交付持续部署Devops CI/CD相关应用介绍JenkinsTekton PipelinesSpinnakerTravis CIGoCD CI/CD相关概念学习 前言 本文主要是介绍一些 CI/CD 相关的概念,通过阅读本文你将快速了解 CI/CD 是什么…...
一、认识STM32
目录 一、初识STM32 1.1 STM32的命名规则介绍 1.2 STM32F103ZET6资源配置介绍 二、如何识别芯片管脚 2.1 如何寻找 IO 的功能说明 三、构成最小系统的要素 一、初识STM32 1.1 STM32的命名规则介绍 以 STM32F103ZET6 来讲解下 STM32 的命名方法: &…...
vue-router的编程式导航有哪些方法?
Vue Router 提供了几种编程式导航的方法,主要包括以下几种: router.push(location, onComplete?, onAbort?):跳转到新的 URL,类似于 <router-link> 的 to 属性。可以指定路径或者命名的路由。 router.replace(location, …...
连接服务器上mysql数据库
1. 首先在服务器的安全组上设置开放3306端口(默认是这个,有自定义可以酌情更改) 2. 更改服务器上的数据库配置文件vi /etc/mysql/my.cnf 增加下面bind- address配置 [mysqld] bind-address0.0.0.0 3. 授予本地IP地址的主机连接权限 - 创建…...
IDEA 中设置 File Header 以及自定义类、方法注释模板的方法
目录 1 设置 File Header2 自定义类、方法注释生成类注解模板生成方法注解模板 1 设置 File Header File -> Settings -> File and Code Templates -> Includes -> File Header -> 编辑 2 自定义类、方法注释 File -> Settings -> Live Templates -&g…...
【数据结构】图的存储结构及实现(邻接表和十字链表)
一.邻接矩阵的空间复杂度 假设图G有n个顶点e条边,则存储该图需要O(n^2) 不适用稀疏图的存储 二.邻接表 1.邻接表的存储思想: 对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(…...
ROS Turtlebot3多机器人编队导航仿真
文章目录 前言一、Gzazebo中加载多台Turtlebot3机器人二、RVIZ中加载多个Turtlebot3机器人三.多机器人编队导航总结 前言 前面已经实现了在gazebo仿真环境中机器人一字型编队、三角形编队、N字型编队等仿真,接下来考虑多机器人编队在编队行进过程中的避障问题&…...
端口配置错误,导致RabbitMq启动报错
SpringBoot启动,报错如下: 2023-11-19 01:33:43.030 UID[] [] [AMQP Connection 116.xxx.xx.xxx:15672] ERROR com.rabbitmq.client.impl.ForgivingExceptionHandler - An unexpected connection driver error occured java.net.SocketException: Sock…...
<MySQL> 什么是JDBC?如何使用JDBC进行编程?
目录 一、JDBC是什么? 二、JDBC常用接口和类 2.1 DataSource 2.2 Connection 2.3 Statement 2.4 ResultSet 三、JDBC的使用 3.1 获得数据库驱动包 3.2 添加到项目依赖 3.3 描述数据库服务器 3.4 建立数据库连接 3.6 执行SQL语句和接收返回数据 3.7 释放…...
基于安卓android微信小程序的装修家装小程序
项目介绍 巧匠家装小程序的设计主要是对系统所要实现的功能进行详细考虑,确定所要实现的功能后进行界面的设计,在这中间还要考虑如何可以更好的将功能及页面进行很好的结合,方便用户可以很容易明了的找到自己所需要的信息,还有系…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
