Skip to content

Commit 2865517

Browse files
update Thread
1 parent d55ccbf commit 2865517

7 files changed

Lines changed: 228 additions & 4 deletions

并发编程/0.1 自问自答.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
(1)假如只有一个 cpu,单核,多线程还有用吗 ?(美团)
22
(2)sychronied 修饰普通方法和静态方法的区别?什么是可见性?
3+
4+
> **可见性是**指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
5+
36
(3)锁分哪几类?
47
(4)CAS 无锁编程的原理。(字节跳动)
58
(5)ReentrantLock 的实现原理。
@@ -23,6 +26,9 @@
2326
(23)线程池管理线程原理。
2427
(24)线程池的相关参数,有哪些类型的线程池,线程池任务如何调度,任务队列只是先进先出的队列吗,任务有优先级怎么办,知道优先级反转吗? (美团)
2528
(25)有三个线程 T1,T2,T3,怎么确保它们按顺序执行?
29+
30+
> [牛客网上的回答](https://www.nowcoder.com/questionTerminal/d8a288fc416c4d638dfb042e1be239fc?toCommentId=86432)
31+
2632
(26)Android 中操作多线程的方式有哪些?
2733
(27)怎样获取当前线程是否是主线程 (字节跳动)
2834
(28)HandlerThread 是什么?

并发编程/3.0 单例:双重检验与并发.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ class Singleton3 {
218218

219219
```
220220

221-
为了解决上面的指令重排问题,需要在 sInstance 上添加 `volatile` 关键字,volatile 可以在保证可见性的同时禁止指令重排,保证了所有的写(write)操作都将发生在读(read)操作之前(write happend befor read).
221+
为了解决上面的==指令重排==问题,需要在 sInstance 上添加 `volatile` 关键字,volatile 可以在保证可见性的同时禁止指令重排,保证了所有的写(write)操作都将发生在读(read)操作之前(write happend befor read).
222222

223223

224224

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
[精选文章](https://www.huaweicloud.com/articles/articles-A-1.html) 面试必问——线程与进程的区别
2+
3+
# 面试必问——线程与进程的区别
4+
5+
6+
7+
*[作者:郝你个建强](https://www.huaweicloud.com/articles/d90c9bf248c8f1731946d65786c95379.html#)* 时间: 2021-02-05 09:59:01
8+
9+
[标签:](https://www.huaweicloud.com/articles/topic-A-1.html)[面试](https://www.huaweicloud.com/articles/topic_1573c74e227f14ded2daa71bb049ecda.html)[多线程](https://www.huaweicloud.com/articles/topic_c9daf4ad6b1b7a1a3e1f2eb57d4d2fc7.html)[操作系统](https://www.huaweicloud.com/articles/topic_30d23ef4f49e85f37f54786ff984032c.html)[线程和进程](https://www.huaweicloud.com/articles/topic_12721f73571f39bf8e1f85317cb5d870.html)
10+
11+
```
12+
【摘要】总结了其他文章,给自己提供一个方便的复习途径 进程: 进程是程序的一次执行过程,是程序在执行过程中的分配和管理资源的基本单位,每个进程都有自己的地址空间,线程至少有 5 种状态:初始态、执行态、等待态、就绪态、终止态。 线程: 线程是CPU调度和分派的基本单位,它可以和同一进程下的其他线程共享全部资源 联系: 线程是进程中的一部分,一个进程可以有多个线程,但线程只能存在于一个进程...
13+
```
14+
15+
[[828企业上云\]注册领8280元礼包 1核2G服务器69元秒杀 **优惠**](https://activity.huaweicloud.com/828_promotion/index.html?organic_arc)
16+
17+
[[免费课程\]Python编程学习路径 **上新**](https://education.huaweicloud.com/programs/2708d678-5dbb-4be2-afc4-88ae69f0dca3/about?isAuth=0&amp?organic)
18+
19+
[[免费套餐\]1核2G 2核4G云服务器免费 云数据库0元试用 **热销**](https://activity.huaweicloud.com/free_test/index.html?organic_arc)
20+
21+
[[开发利器\]开发者专享优惠,解决研发效率、集成难题 **公告**](https://activity.huaweicloud.com/Developer_0820.html?organic_arc)
22+
23+
总结了其他文章,给自己提供一个方便的复习途径
24+
25+
## 进程:
26+
27+
进程是程序的一次执行过程,是程序在执行过程中的分配和管理资源的基本单位,每个进程都有自己的地址空间,线程至少有 5 种状态:初始态、执行态、等待态、就绪态、终止态。
28+
29+
## 线程:
30+
31+
线程是CPU调度和分派的基本单位,它可以和同一进程下的其他线程共享全部资源
32+
33+
## 联系:
34+
35+
线程是进程中的一部分,一个进程可以有多个线程,但线程只能存在于一个进程中。
36+
37+
## 区别:
38+
39+
1. 根本区别:进程是操作系统资源调度的基本单位,线程是任务的调度执行的基本单位
40+
2. 开销方面:进程都有自己的独立数据空间,程序之间的切换开销大;线程也有自己的运行栈和程序计数器,线程间的切换开销较小。
41+
3. 共享空间:进程拥有各自独立的地址空间、资源,所以共享复杂,需要用IPC(Inter-Process Communication,进程间通信),但是同步简单。而线程共享所属进程的资源,因此共享简单,但是同步复杂,需要用加锁等措施。
42+
43+
## 设计线程的原因:
44+
45+
操作系统模型中,进程有两个功能:
46+
47+
1、任务的调度执行基本单位
48+
49+
2、资源的所有权
50+
51+
线程的出现就是将这两个功能分离开来了:thread 执行任务的调度和执行 ; process 资源所有权
52+
53+
这样的好处是:
54+
55+
操作系统中有两个重要概念:并发和隔离
56+
57+
并发:提高硬件利用率,进程的上下文切换比线程的上下文切换效率低,所以线程可以提高并发的效率
58+
59+
隔离:计算机的资源是共享的,当程序发生奔溃时,需要保证这些资源要被回收,进程的资源是独立的,奔溃时不会影响其他程 序的进行,线程资源是共享的,奔溃时整个进程也会奔溃
60+
61+
线程和并发有关系,进程和隔离有关系

并发编程/AsyncTask.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

并发编程/线程池/Java 线程池实现原理及其在美团业务中的实践.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ ThreadPoolExecutor 是如何运行,如何同时维护线程和执行任务的
6767

6868
图 2 ThreadPoolExecutor 运行流程
6969

70-
==线程池在内部实际上构建了一个生产者消费者模型==,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。==线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:(1)直接申请线程执行该任务;(2)缓冲到队列中等待线程执行;(3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收==。
70+
==线程池在内部实际上构建了一个生产者消费者模型==,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。==线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:==
71+
72+
==(1)直接申请线程执行该任务;==
73+
74+
==(2)缓冲到队列中等待线程执行;==
75+
76+
==(3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收==。
7177

7278
接下来,我们会按照以下三个部分去详细讲解线程池运行机制:
7379

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [cloud.tencent.com](https://cloud.tencent.com/developer/article/1513280)
2+
3+
> 在 Android 下,UI 的布局结构,对标到数据结构中,本质就是一个由 View 和 ViewGroup 组成的多叉树结构。其中 View 只能作为叶子节点...
4+
5+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/l3vo6ivz7k.png?imageView2/2/w/1620)
6+
7+
一. 审题
8+
-----
9+
10+
**面试题:**
11+
12+
**给定一个 RootView,打印其内 View Tree 的每个 View。**
13+
14+
在 Android 下,UI 的布局结构,对标到数据结构中,本质就是一个由 View 和 ViewGroup 组成的多叉树结构。其中 View 只能作为叶子节点,而 ViewGroup 是可以存在子节点的。
15+
16+
![img](https://ask.qcloudimg.com/http-save/yehe-4918971/bjr5eltscx.png)
17+
18+
上图就是一个典型的 ViewTree 的结构,而想要遍历这个 ViewTree,还需要用到两个 ViewGroup 的方法。
19+
20+
* `getChildCount()`:获取其子 View 的个数。
21+
* `getChildAt(int)`:获取对应索引的子 View。
22+
23+
对于 View,无需过多处理,直接打印输出即可。而 ViewGroup,除了打印自身的这个节点之外,还需要打印其子节点。
24+
25+
二. 解题的三种实现
26+
----------
27+
28+
### 2.1 递归实现
29+
30+
###
31+
32+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/p4d2w4jaop.png)
33+
34+
**当一个大问题,可以被拆分成多个小问题,并且分解后的小问题,和大问题相比,只是数据规模不同,求解思路完全一致的问题,非常适合递归来实现。**
35+
36+
```
37+
fun recursionPrint(root: View) {
38+
printView(root)
39+
if (root is ViewGroup) {
40+
for (childIndex in 0 until root.childCount) {
41+
val childView = root.getChildAt(childIndex)
42+
recursionPrint(childView)
43+
}
44+
}
45+
}
46+
```
47+
48+
递归确实可以很清晰的实现功能,但是它有一个致命的问题,当递归深度过深的时候,会爆栈。反应在程序上,就是会抛出 `StackOverflowError`这个异常。
49+
50+
面试的时候,面试者解决问题的思路,使用了递归思想,通常都会很自然的问问 JVM 的栈帧,以及为什么会出现 StackOverflowError 异常。
51+
52+
当然这不是本文的重点,大家了解一下即可。
53+
54+
简单来说,每启动一个线程,JVM 都会为其分配一个 Java 栈,每调用一个方法,都会被封装成一个栈帧,进行**压栈**操作,当方法执行完成之后,又会执行**弹栈**操作。而每个栈帧中,当前调用的方法的一些局部变量、动态连接,以及返回地址等数据。
55+
56+
Java 栈和数据结构的栈结构一样,有两个操作,压栈(入栈)、弹栈(出栈),是一个先入后出(FILO)的结构。这一块的东西,延伸出来就比较多了,你可以简单的理解为调用方法就会压栈,方法执行完会弹栈。
57+
58+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/h10ztydlyk.png)
59+
60+
每次方法的调用,执行压栈的操作,但是每个栈帧,都是要消耗内存的。一旦超过了限制,就会爆掉,抛出 StackOverflowError。
61+
62+
递归的代码确实清晰简单,但是问题不少。面试官也不担心面试者写递归代码,后续可以有一连串问题等着。
63+
64+
### 2.2 广度优先实现
65+
66+
前面也提到,这道题本质上就是数据结构中,多叉树的遍历。那最先想到的就是深度优先和广度优先两种遍历策略。
67+
68+
我们先来看看广度优先的实现
69+
70+
广度优先的过程,就是对每一层节点依次访问,访问完了再进入下一层。就是**按树的深度,一层层的遍历访问**
71+
72+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/id06rtebmp.png)
73+
74+
ABCDEFGHI 就是上图这个多叉树,使用广度优先算法的遍历结果。
75+
76+
**广度优先非常适合用先入先出的队列来实现**,每次子 View 都入队尾,而从对头取新的 View 进行处理。
77+
78+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/47589gl30c.png)
79+
80+
代码如下:
81+
82+
```
83+
fun breadthFirst(root :View){
84+
val viewDeque = LinkedList<View>()
85+
var view = root
86+
viewDeque.push(view)
87+
while (!viewDeque.isEmpty()){
88+
view = viewDeque.poll()
89+
printView(view)
90+
if(view is ViewGroup){
91+
for(childIndex in 0 until view.childCount){
92+
val childView = view.getChildAt(childIndex)
93+
viewDeque.addLast(childView)
94+
}
95+
}
96+
}
97+
}
98+
```
99+
100+
这里直接利用 `LinkedList` 来实现队列,它本身就实现了双端队列 `Deque`接口。
101+
102+
### 2.3 深度优先实现
103+
104+
说完广度深度,再继续看看深度优先。
105+
106+
深度优先的过程,就是对每个可能的分支路径,深度到叶子节点,并且每个节点只访问一次。
107+
108+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/s6m19use7k.png)
109+
110+
ADIHCBGFE 就是上图这个多叉树,使用深度优先算法的遍历结果。
111+
112+
在实现上,**深度优先非常适合用先入后出的栈来实现**。逻辑不复杂,直接上执行时,栈的数据变换。
113+
114+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/uvzzx8usas.png)
115+
116+
![](https://ask.qcloudimg.com/http-save/yehe-4918971/qw4k131pwx.png)
117+
118+
代码实现如下:
119+
120+
```
121+
fun depthFirst(root :View){
122+
val viewDeque = LinkedList<View>()
123+
var view = root
124+
viewDeque.push(view)
125+
while (!viewDeque.isEmpty()){
126+
view = viewDeque.pop()
127+
printView(view)
128+
if(view is ViewGroup){
129+
for(childIndex in 0 until view.childCount){
130+
val childView = view.getChildAt(childIndex)
131+
viewDeque.push(childView)
132+
}
133+
}
134+
}
135+
}
136+
```
137+
138+
依然利用 `LinkedList` 来当栈使用,利用 `push()``pop()` 实现栈的逻辑。
139+
140+
三. 小结时刻
141+
-------
142+
143+
今天聊的 View 树的遍历,本质上就是数据结构中,多叉树的遍历,不同的实现方式用来解决不同的问题。
144+
145+
其实这道题,还有一些变种,例如统计 ViewGroup 子 View 的数量、分层打印 ViewTree、查找 ID 为 Xxx 的 View 等,有兴趣可以试着写写代码。
146+
147+
算法题就是这样,有一些是考验编码能力,另一些是解决问题的思路,多思考多写,才是正道。
148+
149+
本文参与[腾讯云自媒体分享计划](https://cloud.tencent.com/developer/support-plan),欢迎正在阅读的你也加入,一起分享。

算法/字节题库.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
**给定 ViewGroup 打印其内所有的 View**
2+
3+
https://cloud.tencent.com/developer/article/1513280
4+

0 commit comments

Comments
 (0)