Skip to content

Commit d55ccbf

Browse files
update Thread
1 parent f2d9610 commit d55ccbf

8 files changed

Lines changed: 840 additions & 24 deletions

并发编程/4.0 Java实现生产者消费者模型.md

Lines changed: 766 additions & 0 deletions
Large diffs are not rendered by default.

并发编程/线程池/Android 的线程和线程池.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ Binder 线程
1010

1111
# Android 中的线程分类
1212

13-
为啥更新UI必须在主线程:因为所有的视图控件都不是线程安全的。
13+
为啥更新UI必须在主线程:
14+
15+
> 根本原因是:所有的视图控件都不是线程安全的。
1416
1517
## AsyncTask
1618

17-
内部使用 Handler + ThreadPool 完成的。关键的点就是**必须要在主线程中启动, 一个实例只能执行一次任务**。在界面销毁的时候一定要手动停止 AsyncTask,否者被持有的对象将不会被 GC。这是因为被线程持有的对象不会被 GC,这是所有线程和线程池的特点。
19+
内部使用 Handler + ThreadPool 完成的。关键的点就是**==必须要在主线程中启动==, ==一个实例只能执行一次任务==**。在界面销毁的时候一定要手动停止 AsyncTask,否者被持有的对象将不会被 GC。这是因为被线程持有的对象不会被 GC,这是所有线程和线程池的特点。
1820

1921
内部有两个线程池,一个用于调度的 SerialExecutor,一个用户执行任务的 THREAD_POOL_EXECUTOR
2022

@@ -66,7 +68,7 @@ Handler 与 Thread 结合的产物,使用 HandlerThread#getHandler() 可以把
6668
}
6769
```
6870

69-
从中可以看出来,HandlerThread 中只有一个消息队列,队列中的消息是顺序执行的,所以 HandlerThread 是线程安全的,但是这样一来影响了一些吞吐量。
71+
从中可以看出来,HandlerThread 中只有一个消息队列,队列中的消息是顺序执行的,所以 ==HandlerThread 是线程安全的,但是这样一来影响了一些吞吐量==
7072

7173

7274

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [tech.meituan.com](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)
22
3+
<font color = red>这篇文章写的很有水平,一定要读一读</font>
4+
35
随着计算机行业的飞速发展,摩尔定律逐渐失效,多核 CPU 成为主流。使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器。J.U.C 提供的线程池:ThreadPoolExecutor 类,帮助开发人员管理线程并方便地执行并行任务。了解并合理使用线程池,是一个开发人员必修的基本功。
46

57
本文开篇简述线程池概念和用途,接着结合线程池的源码,帮助读者领略线程池的设计思路,最后回归实践,通过案例讲述使用线程池遇到的问题,并给出了一种动态化线程池解决方案。
@@ -9,9 +11,9 @@
911

1012
### 1.1 线程池是什么
1113

12-
线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如 MySQL。
14+
线程池(Thread Pool)是一种==基于池化思想管理线程的工具==,经常出现在多线程服务器中,如 MySQL。
1315

14-
线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。
16+
线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。==这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题==,保证了对内核的充分利用。
1517

1618
而本文描述线程池是 JDK 中提供的 ThreadPoolExecutor 类。
1719

@@ -24,7 +26,7 @@
2426

2527
### 1.2 线程池解决的问题是什么
2628

27-
线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:
29+
==线程池解决的核心问题就是资源管理问题==。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:
2830

2931
1. 频繁申请 / 销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
3032
2. 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
@@ -38,9 +40,9 @@
3840

3941
在计算机领域中的表现为:统一管理 IT 资源,包括服务器、存储、和网络资源等等。通过共享资源,使用户在低投入中获益。除去线程池,还有其他比较典型的几种使用策略包括:
4042

41-
1. 内存池 (Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎片。
42-
2. 连接池 (Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。
43-
3. 实例池 (Object Pooling):循环使用对象,减少资源在初始化和释放时的昂贵损耗。
43+
>1. 内存池 (Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎片。
44+
>2. 连接池 (Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。
45+
>3. 实例池 (Object Pooling):循环使用对象,减少资源在初始化和释放时的昂贵损耗。
4446
4547
在了解完 “是什么” 和“为什么”之后,下面我们来一起深入一下线程池的内部实现原理。
4648

并发编程/线程池/线程池.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# 线程池的优点
44

55
* 复用线程池避免因为线程池的创建和销毁所带来的性能开销。
6-
* 能有效的控制线程的最大并发数,避免过多线程抢占资源造成的阻塞。
6+
* 能有效的控制线程的最大并发数,避免过多线程抢占资源造成的阻塞、线程调度消耗过多的系统资源
77
* 能对线程进行管理,并提供定时执行以及指定时间循环功能。
88

99

@@ -83,7 +83,7 @@
8383

8484
ThreadPoolExecutor 执行的大致规则如下:
8585

86-
1. 如果线程池中的线程数量未达到 corePoolSize 则启动一个核心线程来执行任务。
86+
1. ==如果线程池中的线程数量未达到 corePoolSize 则启动一个核心线程来执行任务。==
8787
2. 如果线程池中的线程数量已达到或者超过 corepoolSize ,后续任务将会被插入 workQueue 等待执行
8888
3. 如果在步骤 2 中无法将任务插入 workQueue 中,这往往是任务队列已经满了,这时候如果 corepoolSize < maximumPoolSize ,会立即启动一个非核心线程来执行任务。
8989
4. 如果步骤 3 中线程数量已经达到线程规定的最大值,那么就拒绝任务,此时通过 handler 的 回调通知调用者。
Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[toc]
22

3-
# 基础
43

5-
## 快速排序
4+
5+
# 排序
66

77
```java
88
/**
@@ -92,6 +92,54 @@ public class Solution {
9292
}
9393
```
9494

95+
# [反转链表 II](https://leetcode-cn.com/problems/reverse-linked-list-ii/)
96+
97+
21/8/30
98+
99+
使用 dummpy 节点 + 头插法可以很好接的解决
100+
101+
1、我们定义两个指针,分别称之为 g(guard 守卫) 和 p(point)。
102+
我们首先根据方法的参数 m 确定 g 和 p 的位置。将 g 移动到第一个要反转的节点的前面,将 p 移动到第一个要反转的节点的位置上。我们以 m=2,n=4为例。
103+
2、将 p 后面的元素删除,然后添加到 g 的后面。也即头插法。
104+
3、根据 m 和 n 重复步骤(2)
105+
4、返回 dummyHead.next
106+
107+
<img src="images/1616250561-sZiIjN-img1.png" alt="img1.png" style="zoom: 67%;" />
108+
109+
<img src="images/1617806801-qeWQJb-img2.png" alt="img2.png" style="zoom:67%;" />
110+
111+
```java
112+
class Solution {
113+
public ListNode reverseBetween(ListNode head, int m, int n) {
114+
// 定义一个dummyHead, 方便处理
115+
ListNode dummyHead = new ListNode(0);
116+
dummyHead.next = head;
117+
// 初始化指针
118+
ListNode g = dummyHead;
119+
ListNode p = dummyHead.next;
120+
121+
// 将指针移到相应的位置
122+
for(int step = 0; step < m - 1; step++) {
123+
g = g.next; p = p.next;
124+
}
125+
126+
// 头插法插入节点
127+
for (int i = 0; i < n - m; i++) {
128+
ListNode removed = p.next;
129+
p.next = p.next.next;
130+
131+
removed.next = g.next;
132+
g.next = removed;
133+
}
134+
135+
return dummyHead.next;
136+
}
137+
```
138+
139+
# [排序算法](https://www.nowcoder.com/practice/2baf799ea0594abd974d37139de27896?tpId=194&&tqId=37494&rp=1&ru=/activity/oj&qru=/ta/job-code-high-client/question-ranking)
140+
141+
整体复习资料: [排序算法.md](排序算法.md)
142+
95143

96144

97145
# [LRU缓存机制](https://leetcode-cn.com/problems/lru-cache/)
37.4 KB
Loading
22.3 KB
Loading

算法/排序算法.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
![](images/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1b19ib2tl,size_16,color_FFFFFF,t_70.png)
1616
关于对时间复杂度和空间复杂度的理解,后面会详细讲解
1717

18-
# 冒泡排序(Bubble Sort)
18+
# ⭐️冒泡排序(Bubble Sort)
1919

2020
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
2121
这个算法的名字由来是因为越小的元素会经由交换慢慢 “浮” 到数列的顶端。
@@ -55,15 +55,15 @@ public int[] sortArray(int[] nums) {
5555
**4. 算法分析**
5656
冒泡排序的时间复杂度为 O(n2),空间复杂度为 O(1)。
5757

58-
# 选择排序 (Selection Sort)
58+
# ⭐️选择排序 (Selection Sort)
5959

6060
选择排序 (Selection-sort) 是一种简单直观的排序算法。它的工作原理:
6161
1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
62-
2)然后,再从剩余未排序元素中继续寻找最小(大)元素(所有数据中第二小 / 大),然后放到已排序序列的末尾。
62+
2)然后,再从剩余未排序元素中继续寻找最小(大)元素(所有数据中第二小 / 大),然后放到已排序序列的末尾。
6363
3)以此类推,直到所有元素均排序完毕。
6464

65-
**1. 算法描述**
66-
n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。假设 R[_] 代表有_个数据,具体算法描述如下:
65+
**1. 算法描述**
66+
n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。假设 R[n] 代表有n个数据,具体算法描述如下:
6767

6868
1. 初始状态:无序区为 R[1…n],有序区为空;
6969
2. 第 i 趟排序 (i=1,2,3…n-1) 开始后,当前有序区和无序区分别为 R[1…i]和 R(i+1…n)。
@@ -107,9 +107,7 @@ class Solution {
107107

108108
唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。
109109

110-
# 插入排序(Insertion Sort)
111-
112-
-----------------------
110+
# ⭐️插入排序(Insertion Sort)
113111

114112
插入排序的算法描述是一种简单直观的排序算法。
115113
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
@@ -356,7 +354,7 @@ void shell_sort() {
356354

357355
关于稳定性,在多个分组多次插入排序的情况下可能会改变相同元素的相对位置, 因为它是跳跃排序,可能改变相同元素的前后位置,所以`希尔排序是不稳定的排序,但插入排序是稳定`
358356

359-
# 归并排序(Merge Sort)
357+
# ⭐️归并排序(Merge Sort)
360358

361359
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2路归并。
362360

@@ -445,9 +443,9 @@ void shell_sort() {
445443
**4. 算法分析**
446444
归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn)的时间复杂度。代价是需要额外的内存空间: O(n)
447445

448-
# 快速排序 Quick Sort
446+
# ⭐️快速排序 Quick Sort
449447

450-
快速排序是对冒泡排序的一种改进, 它是不稳定的。由 C. A. R. Hoare 在 1962 年提出的一种划分交换排序,采用的是分治策略(一般与递归结合使用),以减少排序过程中的比较次数,它的最好情况为 O(nlogn),最坏情况为 O(n^2),平均时间复杂度为 O(nlogn)。
448+
快速排序是对冒泡排序的一种改进,==它是不稳定的==。由 C. A. R. Hoare 在 1962 年提出的一种划分交换排序,采用的是分治策略(一般与递归结合使用),以减少排序过程中的比较次数,它的最好情况为 O(nlogn),最坏情况为 O(n^2),平均时间复杂度为 O(nlogn)。
451449

452450
选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小。
453451
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以达到全部数据变成有序。

0 commit comments

Comments
 (0)