Android-多线程

news2024/10/2 12:27:20

线程是进程中可独立执行的最小单位,也是 CPU 资源(时间片)分配的基本单位,同一个进程中的线程可以共享进程中的资源,如内存空间和文件句柄。线程有一些基本的属性,如id、name、以及priority。
id:线程 id 用于标识不同的线程,编号可能被后续创建的线程使用,编号是只读属性,不能修改。
name:线程的名称,默认值是 Thread-(id)
daemon:分为守护线程和用户线程,我们可以通过 setDaemon(true) 把线程设置为守护线程。守护线程通常用于执行不重要的任务,比如监控其他线程的运行情况,GC 线程就是一个守护线程。
setDaemon() 要在线程启动前设置,否则 JVM 会抛出非法线程状态异常,可被继承。
priority:线程调度器会根据这个值来决定优先运行哪个线程(不保证),优先级的取值范围为
1~10,默认值是 5,可被继承。Thread 中定义了下面三个优先级常量:
最低优先级:MIN_PRIORITY = 1
默认优先级:NORM_PRIORITY = 5
最高优先级:MAX_PRIORITY = 10

线程池的原理:
ThreadPoolExecutor线程池的执行方法中,我发现一个出现频率很高的 API addWorker , 线程池拿到任务之后丢给了 Worker 类处理, 这个类还创建了 Thread 对象,就是创建线程的时候,传入的并不是我们给线程池的那个 Runnable 对象,而是 Worker 对象本身,也就是说线程 start 的时候,Worker 类的 run 方法会被执行,然后开启了一个 while 循环,循环的意思是:只要 task 对象不为空,那么就会一直调用 task = getTask(),直到获取到的 task 对象为空了才会停止循环,getTask 其实就是往阻塞队列中取出 Runnable 对象。
线程池复用线程的原理,创建 Thread 对象的时候传入的不是我们的 Runnable 对象,而是通过线程池自定义的 Runnable 类,这个类主要的作用不仅是执行我们的 Runnable 对象,当我们传入的任务被某个线程执行完毕之后,它还会遍历阻塞队列中其他未执行的任务,这样就能达到一个线程执行多个 Runnable 对象的效果,这个就是线程池复用线程的原理。
往线程池添加一个新的任务时,如果核心线程处于空闲状态,任务会直接交由核心线程处理,否则任务会存放到阻塞队列中,当阻塞队列中的任务数量超过设定的最大值时,才会开启非核心线程去执行,如果当前任务总量 > 阻塞队列的最大容量 + 最大线程数时,线程池则会拒绝执行该任务。
在这里插入图片描述
https://www.jianshu.com/p/a6c3df1f12c4

java的内存模型Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
原子性
在Java中,为了保证原子性,提供了两个高级的字节码指令monitorenter和monitorexit。在synchronized的实现原理文章中,介绍过,这两个字节码,在Java中对应的关键字就是synchronized。因此,在Java中可以使用synchronized来保证方法和代码块内的操作是原子性的。
可见性
Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。除了volatile,Java中的synchronized和final两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。
有序性
在Java中,可以使用synchronized和volatile来保证多线程之间操作的有序性。实现方式有所区别:volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只允许一条线程操作。好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可以使用的关键字。读者可能发现了,好像synchronized关键字是万能的,他可以同时满足以上三种特性,这其实也是很多人滥用synchronized的原因。但是synchronized是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。

sleep 和 wait 的区别?
sleep 方法是 Thread 类中的静态方法,wait 是 Object 类中的方法sleep 并不会释放同步锁,而 wait 会释放同步锁sleep 可以在任何地方使用,而 wait 只能在同步方法或者同步代码块中使用sleep 中必须传入时间,而 wait 可以传,也可以不传,不传时间的话只有 notify 或者 notifyAll 才能唤醒,传时间的话在时间之后会自动唤醒

join 的用法
join 方法通常是保证线程间顺序调度的一个方法,它是 Thread 类中的方法。比方说在线程 A 中执行线程 B.join(),这时线程 A 会进入等待状态,直到线程 B 执行完毕之后才会唤醒,继续执行A线程中的后续方法。join 方法可以传时间参数,也可以不传参数,不传参数实际上调用的是 join(0)。它的原理其实是使用了 wait 方法,join 的原理如下:

 public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
 

volatile
一般提到 volatile,就不得不提到内存模型相关的概念。我们都知道,在程序运行中,每条指令都是由 CPU 执行的,而指令的执行过程中,势必涉及到数据的读取和写入。程序运行中的数据都存放在主存中,这样会有一个问题,由于 CPU 的执行速度是要远高于主存的读写速度,所以直接从主存中读写数据会降低 CPU 的效率。为了解决这个问题,就有了高速缓存的概念,在每个 CPU 中都有高速缓存,它会事先从主存中读取数据,在 CPU 运算之后在合适的时候刷新到主存中。这样的运行模式在单线程中是没有任何问题的,但在多线程中,会导致缓存一致性的问题。举个简单的例子:i=i+1 ,在两个线程中执行这句代码,假设i的初始值为0。我们期望两个线程运行后得到2,那么有这样的一种情况,两个线程都从主存中读取i到各自的高速缓存中,这时候两个线程中的i都为0。在线程1执行完毕得到i=1,将之刷新到主存后,线程2开始执行,由于线程2中的i是高速缓存中的0,所以在执行完线程2之后刷新到主存的i仍旧是1。所以这就导致了对共享变量的缓存一致性的问题,那么为了解决这个问题,提出了缓存一致性协议:当 CPU 在写数据时,如果发现操作的是共享变量,它会通知其他 CPU 将它们内部的这个共享变量置为无效状态,当其他 CPU 读取缓存中的共享变量时,发现这个变量是无效的,它会从新从主存中读取最新的值。
把一个变量声明为volatile,其实就是保证了可见性和有序性。 可见性我上面已经说过了,在多线程开发中是很有必要的。这个有序性还是得说一下,为了执行的效率,有时候会发生指令重排,这在单线程中指令重排之后的输出与我们的代码逻辑输出还是一致的。但在多线程中就可能发生问题,volatile在一定程度上可以避免指令重排。volatile的原理是在生成的汇编代码中多了一个lock前缀指令,这个前缀指令相当于一个内存屏障,这个内存屏障有3个作用:确保指令重排的时候不会把屏障后的指令排在屏障前,确保不会把屏障前的指令排在屏障后。修改缓存中的共享变量后立即刷新到主存中。当执行写操作时会导致其他CPU中的缓存无效。

在Java的多线程开发中,有三个重要概念:原子性、可见性、有序性。**原子性:**一个或多个操作要么都不执行,要么都执行。可见性: 一个线程中对共享变量(类中的成员变量或静态变量)的修改,在其他线程立即可见。有序性: 程序执行的顺序按照代码的顺序执行。

volatile和synchronize的区别?
volatile它所修饰的变量不保留拷贝,直接访问主内存中的。在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。 一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。使用场景您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:1)对变量的写操作不依赖于当前值。2)该变量没有包含在具有其他变量的不变式中。volatile最适用一个线程写,多个线程读的场合。如果有多个线程并发写操作,仍然需要使用锁或者线程安全的容器或者原子变量来代替。synchronized当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。区别volatile是变量修饰符,而synchronized则作用于一段代码或方法。volatile只是在线程内存和“主”内存间同步某个变量的值;而synchronized通过锁定和解锁某个监视器同步所有变量的值, 显然synchronized要比volatile消耗更多资源。volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。volatile保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存中和公共内存中的数据做同步。volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。线程安全包含原子性和可见性两个方面,Java的同步机制都是围绕这两个方面来确保线程安全的。关键字volatile主要使用的场合是在多个线程中可以感知实例变量被修改,并且可以获得最新的值使用,也就是多线程读取共享变量时可以获得最新值使用。关键字volatile提示线程每次从共享内存中读取变量,而不是私有内存中读取,这样就保证了同步数据的可见性。

https://juejin.cn/post/6844904136937324552

死锁:
虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件。
1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

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

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

相关文章

搭建LNMP网站平台并部署Web应用

本章主要介绍&#xff1a; 安装Nginx安装MySQL安装PHP在LNMP平台中部署 Web 应用 构建LNMP网站平台就像构建LAMP平台一样&#xff0c;构建LNMP平台也需要Linux服务器&#xff0c;MySQL数据库&#xff0c;PHP解析环境&#xff0c;区别主要在Nginx 与 PHP的协作配置上&#xff0…

Python 全栈体系【四阶】(十三)

第四章 机器学习 十六、模型评估与优化 1. 模型评估 1.1 性能度量 1.1.1 错误率与精度 错误率和精度是分类问题中常用的性能度量指标&#xff0c;既适用于二分类任务&#xff0c;也适用于多分类任务。 错误率&#xff08;error rate&#xff09;&#xff1a;指分类错误的样…

基础数据结构之堆栈

堆栈的定义、入栈、出栈、查询栈顶 #include <stdio.h> #include <stdlib.h>typedef int DataType;// 定义栈节点结构体 struct StackNode;struct StackNode {DataType data; // 节点数据struct StackNode* next; // 指向下一个节点的指针 };// 定…

【数据库原理】(21)查询处理过程

关系型数据库系统的查询处理流程是数据库性能的关键&#xff0c;该流程涉及到将用户的查询请求转化成有效的数据检索操作。通常可以分为四个阶段:查询分析、查询处理、查询优化和查询执行&#xff0c;如图所示。 第一步&#xff1a;查询分析 这个阶段是整个查询处理的起点。数…

基于elementUI封装的带复选框el-checkbox的下拉多选el-select组件

效果图&#xff1a; 组件&#xff1a;MultipleSelect.vue <template><el-select v-model"selectValues" v-bind"$attrs" v-on"listeners" multiple placeholder"请选择" style"width: 50%" change"changeSel…

finalshell查看密码

有小伙伴不清楚finalshell如何查看密码&#xff0c;首先将连接的服务器导出&#xff0c;然后选择要导出的配置文件&#xff0c;将密码编码后的字符串复制运行&#xff0c;详情如下。 1、选中连接的服务器右键&#xff0c;点击“导出”。 2、弹出框选择全部&#xff0c;然后打开…

计算机找不到msvcr100.dll的多种解决方法分享,轻松解决dll问题

msvcr100.dll作为系统运行过程中不可或缺的一部分&#xff0c;它的主要功能在于提供必要的运行时支持&#xff0c;确保相关应用程序能够顺利完成编译和执行。因此&#xff0c;当操作系统或应用程序在运行阶段搜索不到该文件时&#xff0c;自然会导致各类依赖于它的代码无法正常…

类型检测器 FLOW

在很多大型前端框架、插件中都有使用到flow去做类型检测的&#xff08;react、vue、core&#xff09;。 安装flow yarn add flow-bin -dev运行时直接使用 yarn flow会报错提示 执行flow init可能会报错 解决方法&#xff1a; 1.Windows PowerShell.并以管理员身份运行2. 输…

【面试突击】网关系统面试实战

&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308; 欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理…

连续多级主管

背景 组织中一般会有个直接主管&#xff0c;或者汇报主管&#xff0c;有的组织可能有多个主管&#xff0c;更有甚者一个人能可能在不同的业务项目中&#xff0c;这样这个人可能存在n个主管&#xff0c;这样在设计流程中就会衍生出很多问题来。一起看一款审批软件的设置&#x…

vue2-手写轮播图

轮播图5长展示&#xff0c;点击指示器向右移动一个图片&#xff0c;每隔2秒移动一张照片&#xff01; <template><div class"top-app"><div class"carousel-container"><div class"carousel" ref"carousel">&…

FilterQuery过滤查询

ES中的查询操作分为两种&#xff1a;查询和过滤。查询即是之前提到的query查询&#xff0c;它默认会计算每个返回文档的得分&#xff0c;然后根据得分排序。而过滤只会筛选出符合条件的文档&#xff0c;并不计算得分&#xff0c;并且可以缓冲记录。所以我们在大范围筛选数据时&…

2023年全国职业院校技能大赛软件测试赛题—单元测试卷④

任务二 单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应分析处理并显示结果。返回结果“ax&#xff1a;”&#xff08;x为2、3或4&#xff09;&#xff1b;其中变量x、y均须为整型。编写程序代码&#xff0c;使用JUnit框架编写测试类对编写的程序代码…

68.网游逆向分析与插件开发-角色数据的获取-利用蓝量属性分析角色数据基址

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;67.网游逆向分析与插件开发-角色数据的获取-分析角色数据基址-CSDN博客 然后分析任何一个东西&#xff0c;逆向分析的本质就是找东西的意思&#xff0c;找东西核心的观念是内存里得有&#xff0c;就是…

【群晖NAS】记一次FRP报错:login to server failed: connection write timeout

报错如下&#xff1a; rongfuDS224plus:~/fff/frp$ ./frpc -c ./frpc.toml 2024/01/12 23:08:31 [I] [root.go:139] start frpc service for config file [./frpc.toml] 2024/01/12 23:08:41 [W] [service.go:131] login to server failed: i/o deadline reached 2024/01/12 2…

2024 IAA增长变现玩法拆解,NetMarvel提出进阶版攻略!

2023年的国内外市场&#xff0c;很多大甲方都表示消极&#xff0c;字节游戏业务高歌猛进后大撤退更是直接震惊了整个行业&#xff0c;更别说第二第三梯队的服务商了。 动荡和低迷的经济局势还没有消散&#xff0c;这给开发者带来接连不断的挑战。 01 市场反馈是正向的&#x…

Spark SQL基础

SparkSQL基本介绍 什么是Spark SQL Spark SQL是Spark多种组件中其中一个,主要是用于处理大规模的结构化数据 什么是结构化数据: 一份数据, 每一行都有固定的列, 每一列的类型都是一致的 我们将这样的数据称为结构化的数据 例如: mysql的表数据 1 张三 20 2 李四 15 3 王五 1…

每天刷两道题——第十四天

1.1矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用原地算法。 输入&#xff1a;matrix [[0,1,2,0],[3,4,5,2],[1,3,1,5]] 输出&#xff1a;[[0,0,0,0],[0,4,5,0],[0,3,1,0]] 原地算法&#xff08;…

vulnhub靶场之DC-5

一.环境搭建 1.靶场描述 DC-5 is another purposely built vulnerable lab with the intent of gaining experience in the world of penetration testing. The plan was for DC-5 to kick it up a notch, so this might not be great for beginners, but should be ok for p…

YOLOv5改进系列(25)——添加LSKNet注意力机制(大选择性卷积核的领域首次探索)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制 YOLOv5改进系