从0开始深入理解并发、线程与等待通知机制(中)
一,深入学习 Java 的线程
线程的状态/生命周期
Java 中线程的状态分为 6 种:
1. 初始(NEW):新创建了一个线程对象,但还没有调用 start()方法。
2. 运行(RUNNABLE):Java 线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程( 比如 main 线程)调用了该对象的 start() 方法。 该状态的线程位于可运行线程池中,等待被线程调度选中,获取 CPU 的使用权,此时处于就绪状态(ready )。就绪状态的线程在获得 CPU 时间片后变为运行中状态(running )。3. 阻塞(BLOCKED):表示线程阻塞于锁。4. 等待 (WAITING) :进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。5. 超时等待 (TIMED_WAITING) :该状态不同于 WAITING ,它可以在指定的时间后自行返回。6. 终止 (TERMINATED) :表示该线程已经执行完毕。状态之间的变迁如下图所示掌握这些状态可以让我们在进行 Java 程序调优时可以提供很大的帮助。
其他的线程相关方法
yield()方法:使当前线程让出 CPU 占有权,但让出的时间是不可设定的。也 不会释放锁资源。同时执行 yield()的线程有可能在进入到就绪状态后会被操作系统再次选中马上又被执行。(运行态--->就绪态)
比如,ConcurrentHashMap#initTable 方法中就使用了这个方法,
这是因为 ConcurrentHashMap 中可能被多个线程同时初始化 table,但是其实这个时候只允许一个线程进行初始化操作,其他的线程就需要被阻塞或等待, 但是初始化操作其实很快,这里 Doug Lea 大师为了避免阻塞或者等待这些操作 引发的上下文切换等等开销,就让其他不执行初始化操作的线程干脆执行 yield()方法,以让出 CPU 执行权,让执行初始化操作的线程可以更快的执行完成。
线程的优先级
在 Java 线程中,通过一个整型成员变量 priority 来控制优先级,优先级的范 围从 1~10,在线程构建的时候可以通过 setPriority(int)方法来修改优先级,默认优先级是 5,优先级高的线程分配时间片的数量要多于优先级低的线程。
设置线程优先级时,针对频繁阻塞(休眠或者 I/O 操作)的线程需要设置较高优先级,而偏重计算(需要较多 CPU 时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。在不同的 JVM 以及操作系统上,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。
线程的调度
线程调度是指系统为线程分配 CPU 使用权的过程,主要调度方式有两种:
协同式线程调度(Cooperative Threads-Scheduling):
使用协同式线程调度的多线程系统,线程执行的时间由线程本身来控制,线程把自己的工作执行完之后,要主动通知系统切换到另外一个线程上。使用协同式线程调度的最大好处是实现简单,由于线程要把自己的事情做完后才会通知系统进行线程切换,所以没有线程同步的问题,但是坏处也很明显,如果一个线程出了问题,则程序就会一直阻塞。
抢占式线程调度(Preemptive Threads-Scheduling)
使用抢占式线程调度的多线程系统,每个线程执行的时间以及是否切换都由系统决定。在这种情况下,线程的执行时间不可控,所以不会有「一个线程导致整个进程阻塞」的问题出现。
Java 线程调度就是抢占式调度,为什么?
在 Java 中,Thread.yield()可以让出 CPU 执行时间,但是对于获取执行时间, 线程本身是没有办法的。对于获取 CPU 执行时间,线程唯一可以使用的手段是 设置线程优先级,Java 设置了 10 个级别的程序优先级,当两个线程同时处于Ready 状态时,优先级越高的线程越容易被系统选择执行。
线程和协程
为什么 Java 线程调度是抢占式调度?这需要我们了解 Java 中线程的实现模式。
我们已经知道线程其实是操作系统层面的实体,Java 中的线程怎么和操作系统层面对应起来呢?
任何语言实现线程主要有三种方式:
使用内核线程实现(1:1 实现),
使用用户线程实现(1:N 实现),
使用用户线程加轻量级进程混合实现(N:M 实现)。
内核线程实现
使用内核线程实现的方式也被称为 1 : 1 实现。 内核线程( Kernel-LevelThread, KLT ) 就是直接由操作系统内核( Kernel , 下称内核) 支持的线程, 这种线程由内核来完成线程切换, 内核通过操纵调度器( Scheduler ) 对线程进行调度, 并负责将线程的任务映射到各个处理器上。由于内核线程的支持,每个线程都成为一个独立的调度单元,即使其中某一 个在系统调用中被阻塞了,也不会影响整个进程继续工作,相关的调度工作也不需要额外考虑,已经由操作系统处理了。局限性:首先,由于是基于内核线程实现的,所以各种线程操作,如创建、析构及同步,都需要进行系统调用。而系统调用的代价相对较高, 需要在用户态(User Mode)和内核态( Kernel Mode )中来回切换。其次,每个语言层面的线程都需要有一个内核线程的支持,因此要消耗一定的内核资源(如内核线程的栈空间),因此一个系统支持的线程数量是有限的。
用户线程实现
严格意义上的用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知到用户线程的存在及如何实现的。用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。 如果程序实现得当, 这种线程不需要切换到内核态, 因此操作可以是非常快速且低消耗的, 也能够支持规模更大的线程数量, 部分高性能数据库中的多线程就是由用户线程实现的。
用户线程的优势在于不需要系统内核支援,劣势也在于没有系统内核的支援,所有的线程操作都需要由用户程序自己去处理。线程的创建、销毁、切换和调度都是用户必须考虑的问题,而且由于操作系统只把处理器资源分配到进程,那诸如“阻塞如何处理”“多处理器系统中如何将线程映射到其他处理器上”这类问题解决起来将会异常困难, 甚至有些是不可能实现的。 因为使用用户线程实现的程序通常都比较复杂,所以一般的应用程序都不倾向使用用户线程。Java 语言曾经使用过用户线程,最终又放弃了。 但是近年来许多新的、以高并发为卖点的编程语言又普遍支持了用户线程,譬如 Golang。
混合实现
线程除了依赖内核线程实现和完全由用户程序自己实现之外, 还有一种将内核线程与用户线程一起使用的实现方式, 被称为 N:M 实现。 在这种混合实现下, 既存在用户线程, 也存在内核线程。
用户线程还是完全建立在用户空间中, 因此用户线程的创建、 切换、 析构等操作依然廉价, 并且可以支持大规模的用户线程并发。
同样又可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过内核线程来完成。在这种混合模式中, 用户线程与轻量级进程的数量比是不定的,是 N:M 的关系。
Java 线程的实现
Java 线程在早期的 Classic 虚拟机上(JDK 1.2 以前),是用户线程实现的,但从 JDK 1.3 起, 主流商用 Java 虚拟机的线程模型普遍都被替换为基于操作系统原生线程模型来实现,即采用 1: 1 的线程模型。
以 HotSpot 为例,它的每一个 Java 线程都是直接映射到一个操作系统原生线程来实现的,而且中间没有额外的间接结构, 所以 HotSpot 自己是不会去干涉线程调度的,全权交给底下的操作系统去处理。
所以,这就是我们说 Java 线程调度是抢占式调度的原因。而且 Java 中的线程优先级是通过映射到操作系统的原生线程上实现的,所以线程的调度最终取决于操作系统,操作系统中线程的优先级有时并不能和 Java 中的一一对应,所以Java 优先级并不是特别靠谱。
协程 (用户线程的一种实现)
出现的原因
随着互联网行业的发展,目前内核线程实现在很多场景已经有点不适宜了。比如,互联网服务架构在处理一次对外部业务请求的响应, 往往需要分布在不 同机器上的大量服务共同协作来实现,,也就是我们常说的微服务,这种服务细 分的架构在减少单个服务复杂度、 增加复用性的同时, 也不可避免地增加了服 务的数量, 缩短了留给每个服务的响应时间。这要求每一个服务都必须在极短 的时间内完成计算, 这样组合多个服务的总耗时才不会太长;也要求每一个服 务提供者都要能同时处理数量更庞大的请求, 这样才不会出现请求由于某个服务被阻塞而出现等待。
Java 目前的并发编程机制就与上述架构趋势产生了一些矛盾,1:1 的内核线程模型是如今 Java 虚拟机线程实现的主流选择, 但是这种映射到操作系统上的线程天然的缺陷是切换、调度成本高昂,系统能容纳的线程数量也很有限。 以前处理一个请求可以允许花费很长时间在单体应用中,具有这种线程切换的成本也是无伤大雅的, 但现在在每个请求本身的执行时间变得很短、 数量变得很多的前提下,用户本身的业务线程切换的开销甚至可能会接近用于计算本身的开销,这就会造成严重的浪费。
另外我们常见的 Java Web 服务器,比如 Tomcat 的线程池的容量通常在几十个到两百之间, 当把数以百万计的请求往线程池里面灌时, 系统即使能处理得过来,但其中的切换损耗也是相当可观的。
这样的话,对 Java 语言来说,用户线程的重新引入成为了解决上述问题一个非常可行的方案。
其次,Go 语言等支持用户线程等新型语言给 Java 带来了巨大的压力,也使得 Java 引入用户线程成为了一个绕不开的话题。
协程简介
为什么用户线程又被称为协程呢?我们知道,内核线程的切换开销是来自于保护和恢复现场的成本, 那如果改为采用用户线程, 这部分开销就能够省略掉吗? 答案还是“不能”。 但是,一旦把保护、恢复现场及调度的工作从操作系统交到程序员手上,则可以通过很多手段来缩减这些开销。
由于最初多数的用户线程是被设计成协同式调度(Cooperative Scheduling)的,所以它有了一个别名——“协程”(Coroutine) 完整地做调用栈的保护、恢复工作,所以今天也被称为“有栈协程”(Stackfull Coroutine)。
协程的主要优势是轻量, 无论是有栈协程还是无栈协程, 都要比传统内核线程要轻量得多。如果进行量化的话, 那么如果不显式设置,则在 64 位 Linux上 HotSpot 的线程栈容量默认是 1MB,此外内核数据结构(Kernel Data Structures)还会额外消耗 16KB 内存。与之相对的, 一个协程的栈通常在几百个字节到几KB 之间, 所以 Java 虚拟机里线程池容量达到两百就已经不算小了, 而很多支持协程的应用中, 同时并存的协程数量可数以十万计。
协程当然也有它的局限, 需要在应用层面实现的内容(调用栈、 调度器这些)特别多,同时因为协程基本上是协同式调度,则协同式调度的缺点自然在协程上也存在。
总的来说,协程机制适用于被阻塞的,且需要大量并发的场景(网络 io),不适合大量计算的场景,因为协程提供规模(更高的吞吐量),而不是速度(更低的延迟)。
守护线程
Daemon(守护)线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个 Java 虚拟机中不存在非 Daemon 线程的时候,Java 虚拟机将会退出。可以通过调用 Thread.setDaemon(true)将线程设置为 Daemon 线程。我们一般用不上,比如垃圾回收线程就是 Daemon 线程。
Daemon 线程被用作完成支持性工作,但是在 Java 虚拟机退出时 Daemon 线程中的 finally 块并不一定会执行。在构建 Daemon 线程时,不能依靠 finally 块中的内容来确保执行关闭或清理资源的逻辑。
二,线程间的通信和协调、协作
很多的时候,孤零零的一个线程工作并没有什么太多用处,更多的时候,我们是很多线程一起工作,而且是这些线程间进行通信,或者配合着完成某项工作,这就离不开线程间的通信和协调、协作。
管道输入输出流
我们已经知道,进程间有好几种通信机制,其中包括了管道,其实 Java 的线程里也有类似的管道机制,用于线程之间的数据传输,而传输的媒介为内存。
设想这么一个应用场景:通过 Java 应用生成文件,然后需要将文件上传到云端,比如:
1、页面点击导出后,后台触发导出任务,然后将 mysql 中的数据根据导出条件查询出来,生成 Excel 文件,然后将文件上传到 oss,最后发步一个下载文件的链接。
2、和银行以及金融机构对接时,从本地某个数据源查询数据后,上报 xml 格式的数据,给到指定的 ftp 、或是 oss 的某个目录下也是类似的。我们一般的做法是,先将文件写入到本地磁盘,然后从文件磁盘读出来上传到云盘,但是通过 Java 中的管道输入输出流一步到位,则可以避免写入磁盘这一步。Java 中的管道输入 / 输出流主要包括了如下 4 种具体实现:PipedOutputStream PipedInputStream 、 PipedReader 和 PipedWriter ,前两种面向字节,而后两种面向字符。
join 方法
面试题
现在有 T1、T2、T3 三个线程,你怎样保证 T2 在 T1 执行完后执行,T3 在 T2执行完后执行?
答:用 Thread#join 方法即可,在 T3 中调用 T2.join ,在 T2 中调用 T1.join 。join()
把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行。 比如在线程 B 中调用了线程 A 的 Join()方法,直到线程 A 执行完毕后,才会继续执行线程 B 剩下的代码。
synchronized 内置锁
线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码 一步一步地执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立地运行, 那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作, 包括数据之间的共享,协同处理事情。这将会带来巨大的价值。
Java 支持多个线程同时访问一个对象或者对象的成员变量,但是多个线程同 时访问同一个变量,会导致不可预料的结果。关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,使多个线程访问同一个变量的结果正确,它又称为内置锁机制。
相关文章:
从0开始深入理解并发、线程与等待通知机制(中)
一,深入学习 Java 的线程 线程的状态/生命周期 Java 中线程的状态分为 6 种: 1. 初始(NEW):新创建了一个线程对象,但还没有调用 start()方法。 2. 运行(RUNNABLE):Java 线程中将就绪(ready)和…...
UE5报错及解决办法
1、编译报错,内容如下: Unable to build while Live Coding is active. Exit the editor and game, or press CtrlAltF11 if iterating on code in the editor or game 解决办法 取消Enable Live Coding勾选...
怎么通过docker/portainer部署vue项目
这篇文章分享一下如何通过docker将vue项目打包成镜像文件,并使用打包的镜像在docker/portainer上部署运行,写这篇文章参考了vue-cli和docker的官方文档。 首先,阅读vue-cli关于docker部署的说明,上面提供了关键的几个步骤。 从上面…...
【面试经典150 | 矩阵】旋转图像
文章目录 写在前面Tag题目来源题目解读解题思路方法一:原地旋转方法二:翻转代替旋转 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主,并附带…...
机器人制作开源方案 | 家庭清扫拾物机器人
作者:罗诚、李旭洋、胡旭、符粒楷 单位:南昌交通学院 人工智能学院 指导老师:揭吁菡 在家庭中我们有时无法到一些低矮阴暗的地方进行探索,比如茶几下或者床底下,特别是在部分家庭中,如果没有及时对这些阴…...
C++算法 —— 动态规划(8)01背包问题
文章目录 1、动规思路简介2、模版题:01背包第一问第二问优化 3、分割等和子集4、目标和5、最后一块石头的重量Ⅱ 背包问题需要读者先明白动态规划是什么,理解动规的思路,并不能给刚接触动规的人学习。所以最好是看了之前的动规博客࿰…...
ASUS华硕天选4笔记本FA507NU7735H_4050原装出厂Win11系统
下载链接:https://pan.baidu.com/s/1puxQOxk4Rbno1DqxhkvzXQ?pwdhkzz 系统自带网卡、显卡、声卡等所有驱动、出厂主题壁纸、Office办公软件、MyASUS华硕电脑管家、奥创控制中心等预装程序...
金蝶OA server_file 目录遍历漏洞
漏洞描述 金蝶OA server_file 存在目录遍历漏洞,攻击者通过目录遍历可以获取服务器敏感信息 漏洞影响 金蝶OA 漏洞复现 访问漏洞url: 漏洞POC Windows服务器: appmonitor/protected/selector/server_file/files?folderC://&suffi…...
read_image错误
File is no BMP-File(Halcon 错误代码5560)类似的错误一般都是图片内部封装的格式与外部扩展名不一致导致(也就是扩展名并不是真实图片的格式扩展)。 通过软件“UltraEdit”(http://www.onlinedown.net/soft/7752.htm)使用16进制查看&#x…...
文本分词排序
文本分词 在这个代码的基础上 把英语单词作为一类汉语,作为一类然后列出选项 1. 大小排序 2. 小大排序 3. 不排序打印保存代码 import jieba# 输入文本,让我陪你聊天吧~ lines [] print("请输入多行文本,以\"2333.3\"结束&am…...
SQL与关系数据库基本操作
SQL与关系数据库基本操作 文章目录 第一节 SQL概述一、SQL的发展二、SQL的特点三、SQL的组成 第二节 MySQL预备知识一、MySQL使用基础二、MySQL中的SQL1、常量(1)字符串常量(2)数值常量(3)十六进制常量&…...
【2023年11月第四版教材】第18章《项目绩效域》(第一部分)
第18章《项目绩效域》(第一部分) 1 章节内容2 干系人绩效域2.1 绩效要点2.2 执行效果检查2.3 与其他绩效域的相互作用 3 团队绩效域3.1 绩效要点3.2 与其他绩效域的相互作用3.3 执行效果检查3.4 开发方法和生命周期绩效域 4 绩效要点4.1 与其他绩效域的相…...
Docker启动Mysql
如果docker里面没有mysql需要先pull一个mysql镜像 docker pull mysql其中123456是mysql的密码 docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -d mysql可以使用如下命令进入Mysql的命令行界面 docker exec -it mysql bash登录mysql使用如下命令,root是…...
QScrollArea样式
简介 QScrollBar垂直滚动条分为sub-line、add-line、add-page、sub-page、up-arrow、down-arrow和handle几个部分。 QScrollBar水平滚动条分为sub-line、add-line、add-page、sub-page、left-arrow、right-arrow和handle几个部分。 部件如下图所示: 样式详…...
【gitlab】git push -u origin master 报403
问题描述 gitlab版本:14.0.5 虚拟机版本:centos7 项目:renren-fast 原因分析 .git -> config目录下 url配错 但这个url不是手动配置的,还不知道怎么生成。 解决方法 把配置错误的url改成gitlab的project的url 这样&#…...
第二篇:矩阵的翻转JavaScript
一维数组的翻转 // 一维矩阵翻转 // 实例: arr [1,2,3,4,5] > [5,4,3,2,1] let n readline() let arr readline().split( ).map(Number) // console.log(n,arr) let temp 0 for(let i 0; i < n/2;i){temp arr[i]arr[i] arr[n-i-1]arr[n-i-1] temp }…...
代码随想录算法训练营第五十七天 | 动态规划 part 15 | 392.判断子序列、115.不同的子序列
目录 392.判断子序列思路代码 115.不同的子序列思路代码 392.判断子序列 Leetcode 思路 dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]递推公式: 初始化:为0遍历顺序ÿ…...
【国漫逆袭】人气榜,小医仙首次上榜,霍雨浩排名飙升,不良人热度下降
Hello,小伙伴们,我是小郑继续为大家深度解析国漫资讯。 为了提升作品和角色的讨论度,增加平台的用户活跃度,小企鹅推出了动漫角色榜,该榜单以【年】【周】【日】为单位,通过角色的点赞量和互动量进行排名 上周的动漫角…...
国庆中秋特辑(七)Java软件工程师常见20道编程面试题
以下是中高级Java软件工程师常见编程面试题,共有20道。 如何判断一个数组是否为有序数组? 答案:可以通过一次遍历,比较相邻元素的大小。如果发现相邻元素的大小顺序不对,则数组不是有序数组。 public boolean isSort…...
长剖与贪心+树上反悔贪心:1004T4
长剖的本质是一种贪心。(启发式合并本质也是类似哈夫曼树的过程) 在此题中,首先肯定变直径,然后选端点为根。然后选叶子。而每个叶子为了不重复计算,可以只计算其长剖后所在链的贡献。(本题精髓࿰…...
二叉树经典例题
前言: 本文主要讲解了关于二叉树的简单经典的例题。 因为二叉树的特性,所以关于二叉树的大部分题目,需要利用分治的思想去递归解决问题。 分治思想: 把大问题化简成小问题(根节点、左子树、右子树)&…...
什么是指针的指针和指向函数的指针?
理解指针的指针和指向函数的指针对于C语言初学者来说可能会有些挑战,但它们都是非常重要的概念,可以帮助你更好地理解和利用C语言的强大功能。在本文中,我将详细解释这两个概念,包括它们的概念、用途和示例。 指针的指针…...
多个excel合并
目的:将同一个文件下的多个 “京东差评.xlsx” 合并为一个:“京东汇总.xlsx" 代码如下: # -*- coding: utf-8 -*- """ Created on Wed Oct 4 12:52:32 2023author: 64884 """import pandas as pd impor…...
Integrity Plus for Mac,保障网站链接无忧之选
在如今数字化的时代,网站链接的完整性对于用户体验和搜索引擎排名至关重要。如果您是一位网站管理员或者经常需要检查网站链接的人,那么Integrity Plus for Mac(Integrity Plus)将成为您最好的伙伴。 Integrity Plus是一款专业的…...
C#,数值计算——Sobol拟随机序列的计算方法与源程序
1 文本格式 using System; using System.Collections.Generic; namespace Legalsoft.Truffer { /// <summary> /// Sobol quasi-random sequence /// </summary> public class Sobol { public Sobol() { } public static void sobseq(int n,…...
以太网协议介绍(ARP、UDP、ICMP、IP)
以太网协议介绍 一、ARP协议 请求: 应答: ARP协议: 0x0001 0x0800 6 4硬件类型:2个字节,arp协议不仅能在以太网上运行还能在其他类型的硬件上运行。以太网用1来表示; 协议类型:两字节。指的是a…...
【C++】STL详解(十)—— 用红黑树封装map和set
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C学习 🎯长路漫漫浩浩,万事皆有期待 上一篇博客:【C】STL…...
Android学习之路(17) Android Adapter详解
Adapter基础讲解 本节引言 从本节开始我们要讲的UI控件都是跟Adapter(适配器)打交道的,了解并学会使用这个Adapter很重要, Adapter是用来帮助填充数据的中间桥梁,简单点说就是:将各种数据以合适的形式显示到view上,提供 给用户看…...
实验室超声波萃取技术的原理和特点是什么?
梵英超声(fanyingsonic)实验室超声波清洗机 超声波萃取中药材的优越性源于超声波的特殊物理性质。通过压电换能器产生的快速机械振动波,超声波可减少目标萃取物与样品基体之间的作用力,从而实现固液萃取分离。 (1)加速介质质点运…...
用Python操作Word文档,看这一篇就对了!
本文主要讲解Python中操作word的思路。 一、Hello,world! 使用win32com需要安装pypiwin32 pip install pypiwin32 推荐使用python的IDLE,交互方便 1、如何新建文档 from win32com.client import Dispatchapp Dispatch(Word.Application…...
小型公司注册资金写多少合适/无锡优化网站排名
满满的干货!推荐大家购买的一本书,里面很多的内容,都是我编程过程经历过的困惑(相信大家都会遇到),如果早点看到这本书,相信当时我也不会困惑那么久了~所以记录总结一下。 PS.欲看此书ÿ…...
武汉营销推广/关键词优化推广排名
Spark学习笔记总结 02. Spark任务流程 1. RDD的依赖关系 RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency) 窄依赖窄依赖指的是每一个父RDD…...
wordpress仪表盘加载很慢/网推拉新app推广平台
error和exception的区别,RuntimeException和非RuntimeException的区别 1. 异常机制 异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程…...
宁夏做网站的/google登录
题目描述给定两个二叉树,编写一个函数来检验它们是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。示例1:示例2:解题思路二叉树本身就是一种递归的数据结构,所以要判断两个二叉…...
网站英文地图怎么做/网络营销师报名官网
10月27日,华为HiGame于北京举行发展共赢共建拉美游戏合作生态发布会,邀请全国游戏厂商携手共赢拉美市场,近百家合作伙伴与会共襄盛举。 稳步提升的华为消费者云品牌影响力 众所周知,华为在硬件上已成为全球范围内领军品牌之一。20…...
山大优秀网站建设2018年度/竞价推广平台有哪些
作者:GWD 时间:2019.8.27 1、驱动的框架 C库根据不同的swi值进入内核调用sys_open、open_read函数。 2、应用程序怎么和我们的设备对应起来 答:驱动设备框架...