多线程知识汇总

news2024/11/24 3:03:23
IntentService 
多线程的应用在Android  开发中是非常常见的,常用方法主要有:
  • 集成Thread类
  • 实现Runnable接口
  • AsyncTask
  • Handler
  • HandlerThread
  • IntentService
IntentService
定义: Android 里的一个封装类,继承四大组件之一 service
作用:处理异步请求 & 实现多线程
使用场景:线程任务 按照顺序,在后台执行。
        最常见的场景,离线下载,但是不符合多个数据同时请求的场景,所有任务都在同一个Thread looper 里执行。
特别注意
  1. 若启动IntentService多次,那么每个耗时操作则以队列的方式在 IntentService 的 onHandleIntent回调方法中依次执行,执行完自动结束。
问题1:IntentService 如何单独开启1个新的工作线程
@Override
public void onCreate() {
    super.onCreate();
    
    // 1. 通过实例化HandlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
    // HandlerThread继承自Thread,内部封装了 Looper
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
  
    // 2. 获得工作线程的 Looper & 维护自己的工作队列
    mServiceLooper = thread.getLooper();
    // 3. 新建mServiceHandler & 绑定上述获得Looper
    // 新建的Handler 属于工作线程 ->>分析1
    mServiceHandler = new ServiceHandler(mServiceLooper);
}
   /**
     * 分析1:ServiceHandler源码分析
     **/
     private final class ServiceHandler extends Handler {
         // 构造函数
         public ServiceHandler(Looper looper) {
         super(looper);
       }
        // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
        @Override
         public void handleMessage(Message msg) {
  
          // onHandleIntent 方法在工作线程中执行
          // onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
          onHandleIntent((Intent)msg.obj);
          // 执行完调用 stopSelf() 结束服务
          stopSelf(msg.arg1);
    }
}
   /**
     * 分析2: onHandleIntent()源码分析
     * onHandleIntent() = 抽象方法,使用时需重写
     **/
      @WorkerThread
      protected abstract void onHandleIntent(Intent intent);
问题2 IntentService 如何通过onStartCommand() 将intent传递给服务, 依次插入到工作队列中的。
/**
  * onStartCommand()源码分析
  * onHandleIntent() = 抽象方法,使用时需重写
  **/
  public int onStartCommand(Intent intent, int flags, int startId) {
    // 调用onStart()->>分析1
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
  * 分析1:onStart(intent, startId)
  **/
  public void onStart(Intent intent, int startId) {
    // 1. 获得ServiceHandler消息的引用
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    // 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
    //这里的Intent  = 启动服务时startService(Intent) 里传入的 Intent
    msg.obj = intent;
    // 3. 发送消息,即 添加到消息队列里
    mServiceHandler.sendMessage(msg);
}
源码总结:
IntentService 本质 是  service + HanderThread
  1. 通过handlerThread单独开启一个工作线程,IntentService
  2. 创建一个内部Handler:ServiceHandler
  3. 绑定serviceHandler 与 IntentService
  4. 通过onStartCommand() 传递intent到ServiceHandler,依次插入Intent到工作队列中。并且逐个发送到给HanderIntent
  5. 通过onHandlerIntent ()依次处理所有Intent对象所对应的任务。
线程 关键字: Synchronized 
它是java中一个关键字
被他修饰的方法可以保证同一时刻最多只有一个线程执行此方法,其他线程必须等待当前线程执行完该方法/代码块后才能执行该方法。
保证线程安全,解决多线程中的并发同步问题 实现的是阻塞型并发
原理: 
  • 依赖JVM实现同步
  • 底层通过一个监视器对象 monitor 完成,wait()、notify()等方法也依赖于  这个监视器对象 monitor  
monitor 监视器锁的本质,依赖于底层操作系统的互斥锁 实现。
其他控制并发/ 现成同步的方法
Lock       ReentrantLock
CAS   compre and Swap   乐观锁
// CAS的操作参数
内存位置(A)
预期原值(B)
预期新值(C)
// 使用CAS解决并发的原理:
// 1. 首先比较A、B,若相等,则更新A中的值为C、返回True;若不相等,则返回false;
// 2. 通过死循环,以不断尝试尝试更新的方式实现并发
// 伪代码如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
    if(memoryA.get() == oldB){
        memoryA.set(newC);
        return true;
    }
    return false;
}
优点: 耗费资源少,相对于synchronized  ,省去了挂起线程,恢复线程的开销,但是迟迟得不到更新,死循环对CPU资源也是一种浪费
考点:线程与进程的区别
假设:进程 = 桌子,单线程 = 1个人吃饭
  • 单进程、单线程:一个人在一个桌子上吃饭
  • 单进程、多线程:多个人在同一个桌子上一起吃饭
  • 多进程、单线程:多个人每个人在自己的桌子上吃饭
一个进程可以拥有多个线程  ,多个线程可以共享他们隶属的同意进程的系统资源

ThreadLocal

// 1. 直接创建对象
private ThreadLocal myThreadLocal = new ThreadLocal()
// 2. 创建泛型对象
private ThreadLocal myThreadLocal = new ThreadLocal<String>();
// 3. 创建泛型对象 & 初始化值
// 指定泛型的好处:不需要每次对使用get()方法返回的值作强制类型转换
private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
    @Override
    protected String initialValue() {
        return "This is the initial value";
    }
};
// 特别注意:
// 1. ThreadLocal实例 = 类中的private、static字段
// 2. 只需实例化对象一次 & 不需知道它是被哪个线程实例化
// 3. 每个线程都保持 对其线程局部变量副本 的隐式引用
// 4. 线程消失后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)
// 5. 虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程只能访问到自己通过调用ThreadLocal的set()设置的值
// 即 哪怕2个不同的线程在同一个`ThreadLocal`对象上设置了不同的值,他们仍然无法访问到对方的值
// 1. 设置值:set()
// 需要传入一个Object类型的参数
myThreadLocal.set("初始值”);
// 2. 读取ThreadLocal变量中的值:get()
// 返回一个Object对象
String threadLocalValue = (String) myThreadLocal.get();
实现原理:
TheradLocal 类中有一个map(ThreadLocalMap) :  用于存储每个线程 设置的存储在 ThreadLocal 变量的值
1、ThreadLocalMap的键 key = 当前ThreadLocal 实例、 值value = 该线程设置存储在ThreadLocal变量的值
2、该key 是ThreadLocal对象的弱引用,当要抛弃掉 ThreadLocal对象时,垃圾收集器会忽略该key 的引用二清理掉ThreadLoacl对象。
// ThreadLocal的源码
public class ThreadLocal<T> {
    ...
  /**
    * 设置ThreadLocal变量引用的值
    *  ThreadLocal变量引用 指向 ThreadLocalMap对象,即设置ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
    *  ThreadLocalMap的键Key = 当前ThreadLocal实例
    *  ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
    **/  
    public void set(T value) {
      
        // 1. 获得当前线程
        Thread t = Thread.currentThread();
        // 2. 获取该线程的ThreadLocalMap对象 ->>分析1
        ThreadLocalMap map = getMap(t);
        // 3. 若该线程的ThreadLocalMap对象已存在,则替换该Map里的值;否则创建1个ThreadLocalMap对象
        if (map != null)
            map.set(this, value);// 替换
        else
            createMap(t, value);// 创建->>分析2
    }
  /**
    * 获取ThreadLocal变量里的值
    * 由于ThreadLocal变量引用 指向 ThreadLocalMap对象,即获取ThreadLocalMap对象的值 = 该线程设置的存储在ThreadLocal变量的值
    **/
    public T get() {
        // 1. 获得当前线程
        Thread t = Thread.currentThread();
        // 2. 获取该线程的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        // 3. 若该线程的ThreadLocalMap对象已存在,则直接获取该Map里的值;否则则通过初始化函数创建1个ThreadLocalMap对象
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value; // 直接获取值
        }
        return setInitialValue(); // 初始化
    }
  /**
    * 初始化ThreadLocal的值
    **/
    private T setInitialValue() {
        T value = initialValue();
        // 1. 获得当前线程
        Thread t = Thread.currentThread();
        // 2. 获取该线程的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
         // 3. 若该线程的ThreadLocalMap对象已存在,则直接替换该值;否则则创建
        if (map != null)
            map.set(this, value); // 替换
        else
            createMap(t, value); // 创建->>分析2
        return value;
    }
  /**
    * 分析1:获取当前线程的threadLocals变量引用
    **/
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
  /**
    * 分析2:创建当前线程的ThreadLocalMap对象
    **/
    void createMap(Thread t, T firstValue) {
    // 新创建1个ThreadLocalMap对象 放入到 Thread类的threadLocals变量引用中:
        // a. ThreadLocalMap的键Key = 当前ThreadLocal实例
        // b. ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
        t.threadLocals = new ThreadLocalMap(this, firstValue);
        // 即 threadLocals变量 属于 Thread类中 ->> 分析3
    }
  
    ...
}
  /**
    * 分析3:Thread类 源码分析
    **/
    public class Thread implements Runnable {
       ...
       ThreadLocal.ThreadLocalMap threadLocals = null;
       // 即 Thread类持有threadLocals变量
       // 线程类实例化后,每个线程对象拥有独立的threadLocals变量变量
       // threadLocals变量在 ThreadLocal对象中 通过set() 或 get()进行操作
       ...
}
问题: ThreadLoacl 如何做到现成安全的
  • 每个线程拥有自己独立的Threadlocals变量,也就是ThreadLocalMap
  • 每当线程访问ThreadLocals变量时,访问的都是各自线程自己的 ThreadLocalMap变量(键值对)
  • ThreadLocalMap变量的键key = 唯一 = ThreadLocal 实例
问题:TheadLocal 与同步机制的区别
线程池 ThreadPool
定义: 一块缓存了一定线程数量的区域
作用:复用线程  管理线程(统一分配,监控,  控制线程池最大并发数)
优点:降低因线程的创建 销毁所带来的性能开销 - 可以重用缓存在线程池的线程。提高线程响应速度,执行效率。提高线程管力度
线程池有六个核心参数
// 创建线程池对象如下
// 通过 构造方法 配置核心参数
   Executor executor = new ThreadPoolExecutor(
                                              CORE_POOL_SIZE,
                                              MAXIMUM_POOL_SIZE,
                                              KEEP_ALIVE,
                                              TimeUnit.SECONDS,
                                              sPoolWorkQueue,
                                              sThreadFactory
                                               );
// 构造函数源码分析
    public ThreadPoolExecutor (int corePoolSize,
                               int maximumPoolSize,
                               long keepAliveTime,
                               TimeUnit unit,
                               BlockingQueue<Runnable workQueue>,
                               ThreadFactory threadFactory )
——————————————————————————————————————————————————————————————————————————————
// 1. 创建线程池
   // 创建时,通过配置线程池的参数,从而实现自己所需的线程池
   Executor threadPool = new ThreadPoolExecutor(
                                              CORE_POOL_SIZE,
                                              MAXIMUM_POOL_SIZE,
                                              KEEP_ALIVE,
                                              TimeUnit.SECONDS,
                                              sPoolWorkQueue,
                                              sThreadFactory
                                              );
    // 注:在Java中,已内置4种常见线程池,下面会详细说明
// 2. 向线程池提交任务:execute()
    // 说明:传入 Runnable对象
       threadPool.execute(new Runnable() {
            @Override
            public void run() {
                ... // 线程执行任务
            }
        });
// 3. 关闭线程池shutdown()
  threadPool.shutdown();
  
  // 关闭线程的原理
  // a. 遍历线程池中的所有工作线程
  // b. 逐个调用线程的interrupt()中断线程(注:无法响应中断的任务可能永远无法终止)
  // 也可调用shutdownNow()关闭线程:threadPool.shutdownNow()
  // 二者区别:
  // shutdown:设置 线程池的状态 为 SHUTDOWN,然后中断所有没有正在执行任务的线程
  // shutdownNow:设置 线程池的状态 为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
  // 使用建议:一般调用shutdown()关闭线程池;若任务不一定要执行完,则调用shutdownNow()
根据不同参数的不同配置,java中最常见的线程池有四种
  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)

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

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

相关文章

Android 跨进程通信并传输复杂数据

前言 AIDL (Android Interface Definition Language) 支持以下数据类型&#xff1a; 基本数据类型&#xff1a;int、long、float、double、boolean、char、byte。 字符串类型&#xff1a;String。 集合类型&#xff1a;List、Map、Set。 Parcelable 类型&#xff1a;实现了 Par…

linux 文件锁

建议锁,强制锁,记录锁的概念 建议锁&#xff1a; 如果某一个进程对一个文件持有一把锁之后&#xff0c;其他进程仍然可以直接对文件进行操作(open, read, write)而不会被系统禁止&#xff0c;即使这个进程没有持有锁。只是一种编程上的约定。建议锁只对遵守建议锁准则的进程生…

@JsonFormat、@JSONField、@DateTimeFormat详细解说

JsonFormat、JSONField、DateTimeFormat详细解说_jsonfield format_xinlianluohan的博客-CSDN博客 三者出处 1、JsonFormat来源于jackson&#xff0c;Jackson是一个简单基于Java应用库&#xff0c;Jackson可以轻松的将Java对象转换成json对象和xml文档&#xff0c;同样也可以…

CSS SASS calc() 计算表达式或使用变量

calc&#xff08;&#xff09;是css的一个函数&#xff0c;可用于元素计算长度&#xff0c;比如div宽度想要减去一个固定宽度后并自适应&#xff0c;可以写为calc(100% - 60px) 注意“-”两边有空格 sass已经是常用的预编译语言&#xff0c;允许使用变量等规则&#xff0c;如果…

上海亚商投顾:沪指窄幅震荡微跌 两市成交金额创年内新低

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日窄幅震荡&#xff0c;创业板指盘中跌超1%&#xff0c;黄白二线有所分化。华为星闪概念股午后拉升&…

EPC与5GC/5GS互联互通

一、5GS与EPC/E-UTRAN互通的非漫游架构 1&#xff0e;N26接口是MME和5GS AMF之间的CN间接口&#xff0c;以实现EPC和NG核心之间的互通。网络中支持N26接口是可选的&#xff0c;用于互通。N26支持在S10上支持的功能的子集&#xff08;对于互通是必要的&#xff09;。 2&#xf…

面试题:HTTPS 是如何保证传输安全的?又被问了!

文章目录 1. HTTP 协议1.1 HTTP 协议介绍1.2 HTTP 中间人攻击1.3 防止中间人攻击 2. HTTPS 协议2.1 HTTPS 简介2.2 CA 认证体系 总结 1. HTTP 协议 在谈论 HTTPS 协议之前&#xff0c;先来回顾一下 HTTP 协议的概念。 1.1 HTTP 协议介绍 HTTP 协议是一种基于文本的传输协议&…

Android13 大屏设备底部显示TaskBar并NavagatonBar居右

Android 13大屏设备时底下显示任务栏以及虚拟按键靠右的问题&#xff0c; 当前需求是去掉底部任务栏的显示&#xff0c;并把虚拟按键导航栏居中显示。 修改前的效果&#xff1a; 修改后的效果&#xff1a; 通过查看源码逻辑&#xff0c;可以发现只需把isTablet相关的逻辑和…

Mybatis 映射器中映射方法接受多个参数(@Param)

前面我们介绍了使用Mybatis映射器进行数据的增删改查操作&#xff1b;本篇我们继续介绍在Mybatis映射器的映射方法中如何使用多个参数。 如果您对Mybatis使用映射器进行数据的增删改查操作不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; …

【数据结构】二叉树的构建与基本操作实现

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.前序建立二叉树 2.销毁二叉树 3.统计 …

uni-app 之 picker选择器

uni-app 之 picker选择器 同步滚动&#xff1a;开 uni-app 之 picker选择器 一、普通选择器 二、多列选择器 三、时间选择器 四、日期选择器 一、普通选择器 <template><view><picker change"bindPickerChange" :value"index" :range&q…

LabVIEW开发航天器模拟器的姿态控制和反作用轮动量管理

LabVIEW开发航天器模拟器的姿态控制和反作用轮动量管理 在过去十年中&#xff0c;航天器一直是现代技术进步的先决条件。迄今为止&#xff0c;为了更好地完成各种实际任务&#xff0c;已经在航天器姿态控制领域进行了大量研究。航天器一旦进入太空&#xff0c;就容易出现不确定…

JavaWeb开发-05-SpringBootWeb请求响应

一.请求 1.Postman 2.简单参数 ​ package com.wjh.controller;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;/** 测试请求参数接受*/ R…

【IntelliJ IDEA】cmd和idea Terminal查看java版本不一致

问题描述 原来win10电脑上安装的是jdk8的版本&#xff0c;因某些原因&#xff0c;现在想换成jdk7的版本&#xff0c;修改环境变量后&#xff0c;在cmd中执行 [java -version]命令&#xff0c;显示的是7的版本。 但在idea的Terminal中执行&#xff0c;确实显示8的版本。 原因分…

vue设置路由模式为history,打包部署,并解决404问题

现在Router配置里面加上 base 和 mode 属性&#xff1a; export default new Router({base: /your_project_name/,mode: history,routes: [......] })这样就能支持 history 模式了&#xff0c;但是现在静态资源获取还有问题。 解决静态资源获取问题 在 config/index.js 文件…

TS中的数据类型

一、number类型 let c: number; c 10; c "hello"; // 不能复制string类型 二、string类型 let d: string; d "hello"; d 10; // 不能复制number类型 三、boolean类型 let e: boolean true; e false; e 10; // 不能赋值true和false以外的值 四…

Cypress安装与使用教程(1)—— 软测大玩家

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

半导体划片机工艺应用

半导体划片工艺是半导体制造过程中的重要步骤之一&#xff0c;主要用于将大尺寸的晶圆切割成小片&#xff0c;以便进行后续的制造和封装过程。以下是一些半导体划片工艺的应用&#xff1a; 晶圆划片&#xff1a;在半导体制造过程中&#xff0c;需要将大尺寸的晶圆切割成小片&am…

【数据库系统概论】数据库系统外部的体系结构

单用户结构主从式结构分布式结构客户机&#xff0f;服务器结构&#xff08;C/S结构&#xff09;浏览器 / 服务器结构&#xff08;B/S结构&#xff09;感谢 &#x1f496; 上一篇文章 数据库系统的三级模式和二级映射介绍的是数据库系统内部的体系结构&#xff0c;是从应用开发…

计算机网络知识补充(1)

计算机网络:是一个将分散的&#xff0c;具有独立功能的计算机系统&#xff0c;通过通信设备和线路进行连接起来&#xff0c;由功能完善的软件实现资源共享和信息共享的系统&#xff0c;计算机网络是互连的&#xff0c;自治的计算机集合 互连:通过通信链路来进行互联互通 自治:没…