Android并发编程与多线程

news2024/12/23 10:46:39

   一、Android线程基础

1.线程和进程

  • 一个进程最少一个线程,进程可以包含多个线程
  • 进程在执行过程中拥有独立的内存空间,而线程运行在进程内

2.线程的创建方式

  • new Thread:

        缺点:缺乏统一管理,可能无限制创建线程,相互之间竞争,可能占用过多的系统资源导致死机或OOM

    new Thread(new Runnable() {
        @Override
        public void run() {

        }
    }).start();

    class MyThread extends Thread{
        @Override
        public 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();
        //尝试获取锁,成功返回true
        lock.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,线程复用存活时间为60s
        Executors.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的工作任务

  1. 检查线程池状态,能否继续创建线程
  2. 把runnable封装成worker,添加到工作队列
  3. 启动新建的线程
  4. 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;
                }
            }
        }

五、协程

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

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

相关文章

msvcp140.dll是什么东西以及如何解决其文件缺失问题

当我们在使用Windows电脑的过程中&#xff0c;有时候可能会遇到一些由于系统文件缺失或者损坏而导致的问题。其中&#xff0c;"msvcp140.dll缺失"就是一种常见的错误提示。msvcp140.dll究竟是什么&#xff1f;为什么它会缺失&#xff1f;又该如何解决这个问题呢&…

国民技术Cortex-M0系列单片机IAP升级

考虑到设备部署到现场后有可能需要进行软件升级&#xff0c;之前做过PIC系列单片机的升级&#xff0c;现在想做个国民技术N32G031系列Cortex-M0内核的单片机IAP方案。 因为国民技术系列单片机在很多大程度上都模仿了STM32&#xff0c;所以我想其升级方案极有可能差不多。于是在…

微软宣布计划在 Windows 10 版本 22H2 中引入 AI 助手 Copilot

根据之前的传言&#xff0c;微软宣布计划在 Windows 10 版本 22H2 中引入 AI 助手 Copilot。Copilot 将包含在 Windows 10 家庭版和专业版中。该更新的发布日期尚未公布&#xff0c;但预计将在不久的将来发布。 在一份新闻稿中&#xff0c;微软表示在向 Windows 11 用户提供 Co…

2.Pandas数据预处理

2.1 数据清洗 以titanic数据为例。 df pd.read_csv(titanic.csv) 2.1.1 缺失值 &#xff08;1&#xff09;缺失判断 df.isnull() &#xff08;2&#xff09;缺失统计 # 列缺失统计 df.isnull().sum(axis0) # 行缺失统计 df.isnull().sum(axis1) # 统计缺失率 df.isnu…

时间序列预测中的4大类8种异常值检测方法(从根源上提高预测精度)

一、本文介绍 本文给大家带来的是时间序列预测中异常值检测&#xff0c;在我们的数据当中有一些异常值&#xff08;Outliers&#xff09;是指在数据集中与其他数据点显著不同的数据点。它们可能是一些极端值&#xff0c;与数据集中的大多数数据呈现明显的差异。异常值可能由于…

小红书美妆文案怎么写会火?

美妆护肤品牌如何在众多竞争者中脱颖而出&#xff0c;成为消费者心中的“网红”&#xff1f;答案就在小红书这个国内知名的美妆护肤分享平台上。小红书美妆文案怎么写会火&#xff1f;本文伯乐网络传媒将为你揭秘小红书美妆爆款文案撰写技巧&#xff0c;让你的内容疯狂吸睛&…

Java入门篇 之 多态

本篇碎碎念&#xff1a;个人认为&#xff0c;一切都不是不学习的理由&#xff0c;在如今这个"内卷"的时代&#xff0c;唯有认真学习&#xff0c;掌握知识&#xff0c;才能把握命运 今日份励志文案:你必须拼尽全力&#xff0c;才有资格说运气不好 加油吧&#xff0c;少…

微服务学习 | Eureka注册中心

微服务远程调用 在order-service的OrderApplication中注册RestTemplate 在查询订单信息时&#xff0c;需要同时返回订单用户的信息&#xff0c;但是由于微服务的关系&#xff0c;用户信息需要在用户的微服务中去查询&#xff0c;故需要用到上面的RestTemplate来让订单的这个微…

C语言之break continue详解

C语言之break continue 文章目录 C语言之break continue1. break 和 continue2. while语句中的break和continue2.1break和continue举例 3. for语句中的break和continue3.1break和continue举例 1. break 和 continue 循环中break和continue 在循环语句中&#xff0c;如果我达到…

软件开发和测试

一&#xff0c;敏捷软件开发 二&#xff0c;软件测试

【C++】多线程的学习笔记(3)——白话文版(bushi

前言 好久没有继续写博客了&#xff0c;原因就是去沉淀了一下偷懒了一下 现在在学网络编程&#xff0c;c的多线程也还在学 这一变博客就讲讲c中的Condition Variable库吧 Condition Variable的简介 官方原文解释 翻译就是 条件变量是一个对象&#xff0c;它能够阻止调用…

微电影分销付费短剧小程序开发

微电影系统分销管理付费软件是一款面向微电影制作公司和影视产业的付费软件&#xff0c;它的出现旨在帮助微电影制作公司和影视产业实现分销管理&#xff0c;提高产业的效率和竞争力。本文将介绍微电影系统分销管理付费软件的背景、特点和开发方法。 一、背景 微电影作…

苍穹外卖项目笔记(2)

1 Nginx 反向代理和负载均衡 1.1 概念 【Tips】可以看到前端请求地址和后端接口地址并不匹配&#xff0c;这里涉及到 nginx 反向代理 &#xff0c;就是将前端发送的动态请求由 nginx 转发到后端服务器 使用 nginx 作反向代理的好处&#xff1a; 提高访问速度&#xff08;在请…

腾讯云服务器新用户购买优惠多少钱?腾讯云新用户优惠信息来了!

腾讯云服务器新用户购买优惠多少钱&#xff1f;这是每个新手上路的人都会问到的问题。 如果你是一个刚刚接触云服务器的小白用户&#xff0c;不知道该如何选择合适的云服务器&#xff0c;那么你就来对了地方。今天我们将向你介绍腾讯云服务器新用户购买优惠活动&#xff0c;让…

结构体数组保存进二进制文件的简单做法

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 最近面临这样一个需求&#xff1a;以比较节省存储空间的存储一组坐标点到文件&#xff0c;要求程序能够跨平台读写这种文件。思考了一下&#xff0c;比较…

2023年北京市安全员-A证证模拟考试题库及北京市安全员-A证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年北京市安全员-A证证模拟考试题库及北京市安全员-A证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;北京市安全员-A证证模拟考试题库是根据北京市安全员-A证最新版教材&#xff0c;北京市安全员-A证大…

Linux基本指令及周边(第一弹)

文章目录 前言mkdir指令&#xff08;重要&#xff09;&#xff1a;tree指令rmdir指令 && rm 指令(重要&#xff09;&#xff1a;touch指令ls指令pwd指令cd 指令用户家目录man指令&#xff08;重要&#xff09;&#xff1a;mv指令&#xff08;重要&#xff09;cat指令绝…

腾讯云服务器秒杀什么时候开始?腾讯云服务器秒杀时间

腾讯云服务器秒杀什么时候开始呢&#xff1f;我们一起来揭晓答案&#xff01; 腾讯云服务器秒杀活动即日起至2023-11-30 23:59:59&#xff0c;每日0点限量秒杀。这意味着&#xff0c;每一天的开始&#xff0c;你都有机会抢到心仪的服务器。秒杀活动入口&#xff1a;https://te…

使用Ant Design Pro开发时的一个快速开发接口请求的技巧

使用Ant Design Pro开发时的一个快速开发接口的技巧 当我们的后端在写好接口以后&#xff0c;我们通过swagger knife4j可以生成一个接口文档&#xff0c;后端启动以后&#xff0c;可以生成一个接口文档&#xff0c;当输入地址 localhost:8101/api/v3/api-docs &#xff08;这…

image图片之间的间隙消除

多个图片排列展示&#xff0c;水平和垂直方向的间隔如何消除 垂直方向 vertical-align 原因&#xff1a; vertical-align属性主要用于改变行内元素的对齐方式&#xff0c;行内元素默认垂直对齐方式是基线对齐&#xff08;baseline&#xff09; 这是因为图片属于行内元素&…