Skip to content

Commit 93ed497

Browse files
committed
📝 Writing docs.
1 parent 57002c5 commit 93ed497

1 file changed

Lines changed: 113 additions & 86 deletions

File tree

docs/concurrent/线程基础.md

Lines changed: 113 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,27 @@ tags:
1212

1313
<!-- TOC depthFrom:2 depthTo:3 -->
1414

15-
* [线程生命周期](#线程生命周期)
16-
* [创建线程](#创建线程)
17-
* [继承 Thread 类](#继承-thread-类)
18-
* [实现 Runnable 接口](#实现-runnable-接口)
19-
* [通过 Callable 接口和 Future 接口](#通过-callable-接口和-future-接口)
20-
* [三种创建线程方式对比](#三种创建线程方式对比)
21-
* [Thread 中的重要方法](#thread-中的重要方法)
22-
* [设置/获取线程名称](#设置获取线程名称)
23-
* [判断线程是否启动](#判断线程是否启动)
24-
* [中断线程](#中断线程)
25-
* [守护线程](#守护线程)
26-
* [设置/获取线程优先级](#设置获取线程优先级)
27-
* [线程的强制执行](#线程的强制执行)
28-
* [线程的休眠](#线程的休眠)
29-
* [线程的礼让](#线程的礼让)
15+
* [线程简介](#线程简介)
16+
* [什么是线程](#什么是线程)
17+
* [线程生命周期](#线程生命周期)
18+
* [启动和终止线程](#启动和终止线程)
19+
* [构造线程](#构造线程)
20+
* [继承 Thread 类](#继承-thread-类)
21+
* [实现 Runnable 接口](#实现-runnable-接口)
22+
* [实现 Callable 接口](#实现-callable-接口)
23+
* [三种创建线程方式对比](#三种创建线程方式对比)
24+
* [Thread 中的重要方法](#thread-中的重要方法)
25+
* [设置/获取线程名称](#设置获取线程名称)
26+
* [判断线程是否启动](#判断线程是否启动)
27+
* [中断线程](#中断线程)
28+
* [守护线程](#守护线程)
29+
* [设置/获取线程优先级](#设置获取线程优先级)
30+
* [线程的强制执行](#线程的强制执行)
31+
* [线程的休眠](#线程的休眠)
3032
* [线程的终止](#线程的终止)
31-
* [Object 中的并发方法](#object-中的并发方法)
33+
* [线程间通信](#线程间通信)
3234
* [wait/notify/notifyAll](#waitnotifynotifyall)
35+
* [线程的礼让](#线程的礼让)
3336
* [FAQ](#faq)
3437
* [start() 和 run() 有什么区别?可以直接调用 Thread 类的 run() 方法么?](#start-和-run-有什么区别可以直接调用-thread-类的-run-方法么)
3538
* [sleep()、yield()、join() 方法有什么区别?为什么 sleep()和 yield()方法是静态的?](#sleepyieldjoin-方法有什么区别为什么-sleep和-yield方法是静态的)
@@ -42,11 +45,24 @@ tags:
4245

4346
<!-- /TOC -->
4447

45-
## 线程生命周期
48+
## 线程简介
49+
50+
### 什么是线程
51+
52+
现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。
53+
54+
### 线程生命周期
55+
56+
<p align="center">
57+
<img src="https://raw.githubusercontent.com/dunwu/javase-notes/master/images/concurrent/thread-state.png">
58+
</p>
59+
60+
线程的生命周期有 6 种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。
61+
62+
以下是 6 种状态的说明,以及状态间的联系。
4663

4764
* 开始(New):如果创建 Thread 类的实例,但在调用 start() 方法之前,线程处于新状态。
4865
* 可运行(Runnable):线程对象创建后,其他线程(比如 main 线程)调用了该对象的 start()方法。此状态意味着,线程已经准备好了,一旦被线程调度器分配了 CPU 时间片,就可以运行线程。
49-
* 运行(Running):可运行状态(Runnable)的线程获得了 cpu 时间片(timeslice),执行程序代码。
5066
* 定时等待(Timed waiting):定时等待是在指定等待时间内等待的线程状态。调用以下方法,线程会进入定时等待状态:
5167
* Thread.sleep(sleeptime)
5268
* Object.wait(timeout)
@@ -60,25 +76,25 @@ tags:
6076
* 阻塞(Blocked):阻塞状态。线程阻塞的线程状态等待监视器锁定。处于阻塞状态的线程正在等待监视器锁定,以便在调用 Object.wait 之后输入同步块/方法或重新输入同步块/方法。
6177
* 终止(Terminated):线程 run()、main() 方法执行结束,或者因异常退出了 run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
6278

63-
线程状态切换如下图所示:
79+
## 启动和终止线程
6480

65-
<p align="center">
66-
<img src="https://raw.githubusercontent.com/dunwu/javase-notes/master/images/concurrent/thread-states.png" alt="thread-states">
67-
</p>
81+
### 构造线程
6882

69-
## 创建线程
83+
构造线程主要有三种方式
7084

71-
创建线程主要有三种方式
85+
* 继承 `Thread`
86+
* 实现 `Runnable` 接口
87+
* 实现 `Callable` 接口
7288

73-
* 继承 Thread 类
74-
* 实现 Runnable 接口
75-
* 通过 Callable 接口和 Future 接口
89+
#### 继承 Thread 类
7690

77-
### 继承 Thread
91+
通过继承 Thread 类构造线程的步骤:
7892

79-
* 定义 Thread 类的子类,并重写该类的 run 方法,该 run 方法的方法体就代表了线程要完成的任务。因此把 run()方法称为执行体。
93+
* 定义 Thread 类的子类,并重写该类的 run() 方法,该 run() 方法的方法体就代表了线程要完成的任务。因此把 run() 方法称为执行体。
8094
* 创建 Thread 子类的实例,即创建了线程对象。
81-
* 调用线程对象的 start()方法来启动该线程。
95+
* 调用线程对象的 start() 方法来启动该线程。
96+
97+
示例:
8298

8399
```java
84100
public class ThreadDemo02 {
@@ -110,11 +126,15 @@ public class ThreadDemo02 {
110126
}
111127
```
112128

113-
### 实现 Runnable 接口
129+
#### 实现 Runnable 接口
130+
131+
通过实现 Runnable 接口构造线程的步骤:
114132

115-
* 定义 Runnable 接口的实现类,并重写该接口的 run()方法,该 run()方法的方法体同样是该线程的线程执行体。
133+
* 定义 Runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。
116134
* 创建 Runnable 实现类的实例,并依此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
117-
* 调用线程对象的 start()方法来启动该线程。
135+
* 调用线程对象的 start() 方法来启动该线程。
136+
137+
示例:
118138

119139
```java
120140
public class RunnableDemo {
@@ -147,12 +167,16 @@ public class RunnableDemo {
147167
}
148168
```
149169

150-
### 通过 Callable 接口和 Future 接口
170+
#### 实现 Callable 接口
151171

152-
* 创建 Callable 接口的实现类,并实现 call()方法,该 call()方法将作为线程执行体,并且有返回值。
153-
* 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call()方法的返回值。
172+
通过实现 Callable 接口构造线程的步骤:
173+
174+
* 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
175+
* 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
154176
* 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
155-
* 调用 FutureTask 对象的 get()方法来获得子线程执行结束后的返回值
177+
* 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
178+
179+
示例:
156180

157181
```java
158182
public class CallableAndFutureDemo {
@@ -171,12 +195,12 @@ public class CallableAndFutureDemo {
171195
}
172196
```
173197

174-
### 三种创建线程方式对比
198+
#### 三种创建线程方式对比
175199

176-
* 实现 Runnable 接口优于继承 Thread 类,因为实现接口更便于扩展;
177-
* 实现 Runnable 接口的线程没有返回值;而使用 Callable Future 方式可以让线程有返回值
200+
* **实现 Runnable 接口优于继承 Thread 类**,因为实现接口方式更便于扩展类。
201+
* 实现 Runnable 接口的线程没有返回值;**实现 Callable 接口的线程有返回值**
178202

179-
## Thread 中的重要方法
203+
### Thread 中的重要方法
180204

181205
* `run` - 线程的执行实体。
182206
* `start` - 线程的启动方法。
@@ -189,7 +213,7 @@ public class CallableAndFutureDemo {
189213
* `Thread.sleep` - 使用 Thread.sleep() 方法即可实现休眠。
190214
* `Thread.yield` - 可以使用 Thread.yield() 方法将一个线程的操作暂时让给其他线程执行。
191215

192-
### 设置/获取线程名称
216+
#### 设置/获取线程名称
193217

194218
在 Thread 类中可以通过 `setName()``getName()` 来设置、获取线程名称。
195219

@@ -217,7 +241,7 @@ public class ThreadNameDemo {
217241
}
218242
```
219243

220-
### 判断线程是否启动
244+
#### 判断线程是否启动
221245

222246
在 Thread 类中可以通过 `isAlive()` 来判断线程是否启动。
223247

@@ -250,7 +274,7 @@ public class ThreadAliveDemo {
250274
}
251275
```
252276

253-
### 中断线程
277+
#### 中断线程
254278

255279
当一个线程运行时,另一个线程可以直接通过 `interrupt()` 方法中断其运行状态。
256280

@@ -287,7 +311,7 @@ public class ThreadInterruptDemo {
287311
}
288312
```
289313

290-
### 守护线程
314+
#### 守护线程
291315

292316
在 Java 程序中,只要前台有一个线程在运行,则整个 Java 进程就不会消失,所以此时可以设置一个守护线程,这样即使 Java 进程结束了,此守护线程依然会继续执行。可以使用 `setDaemon()` 方法设置线程为守护线程;可以使用 `isDaemon()` 方法判断线程是否为守护线程。
293317

@@ -313,7 +337,7 @@ public class ThreadDaemonDemo {
313337
}
314338
```
315339

316-
### 设置/获取线程优先级
340+
#### 设置/获取线程优先级
317341

318342
在 Java 中,所有线程在运行前都会保持在就绪状态,那么此时,哪个线程优先级高,哪个线程就有可能被先执行。
319343

@@ -357,7 +381,7 @@ public class ThreadPriorityDemo {
357381
}
358382
```
359383

360-
### 线程的强制执行
384+
#### 线程的强制执行
361385

362386
在线程操作中,可以使用 `join()` 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。
363387

@@ -392,7 +416,7 @@ public class ThreadJoinDemo {
392416
}
393417
```
394418

395-
### 线程的休眠
419+
#### 线程的休眠
396420

397421
直接使用 `Thread.sleep()` 方法即可实现休眠。
398422

@@ -428,40 +452,6 @@ public class ThreadSleepDemo {
428452
}
429453
```
430454

431-
### 线程的礼让
432-
433-
在线程操作中,可以使用 `Thread.yield()` 方法将一个线程的操作暂时让给其他线程执行。
434-
435-
```java
436-
public class ThreadYieldDemo {
437-
438-
public static void main(String[] args) {
439-
MyThread t = new MyThread();
440-
new Thread(t, "线程A").start();
441-
new Thread(t, "线程B").start();
442-
}
443-
444-
static class MyThread implements Runnable {
445-
446-
@Override
447-
public void run() {
448-
for (int i = 0; i < 5; i++) {
449-
try {
450-
Thread.sleep(1000);
451-
} catch (Exception e) {
452-
e.printStackTrace();
453-
}
454-
System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
455-
if (i == 2) {
456-
System.out.print("线程礼让:");
457-
Thread.yield();
458-
}
459-
}
460-
}
461-
}
462-
}
463-
```
464-
465455
### 线程的终止
466456

467457
Thread 中的 stop 方法有缺陷,已废弃。
@@ -500,14 +490,16 @@ public class ThreadStopDemo02 {
500490
}
501491
```
502492

503-
## Object 中的并发方法
493+
## 线程间通信
494+
495+
### wait/notify/notifyAll
496+
497+
wait、notify、notifyAll 是 Object 类中的方法。
504498

505499
* `wait` - 线程自动释放其占有的对象锁,并等待 notify。
506500
* `notify` - 唤醒一个正在 wait 当前对象锁的线程,并让它拿到对象锁。
507501
* `notifyAll` - 唤醒所有正在 wait 前对象锁的线程。
508502

509-
### wait/notify/notifyAll
510-
511503
生产者、消费者示例:
512504

513505
```java
@@ -589,6 +581,41 @@ public class ThreadWaitNotifyDemo02 {
589581
}
590582
```
591583

584+
585+
### 线程的礼让
586+
587+
在线程操作中,可以使用 `Thread.yield()` 方法将一个线程的操作暂时让给其他线程执行。
588+
589+
```java
590+
public class ThreadYieldDemo {
591+
592+
public static void main(String[] args) {
593+
MyThread t = new MyThread();
594+
new Thread(t, "线程A").start();
595+
new Thread(t, "线程B").start();
596+
}
597+
598+
static class MyThread implements Runnable {
599+
600+
@Override
601+
public void run() {
602+
for (int i = 0; i < 5; i++) {
603+
try {
604+
Thread.sleep(1000);
605+
} catch (Exception e) {
606+
e.printStackTrace();
607+
}
608+
System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
609+
if (i == 2) {
610+
System.out.print("线程礼让:");
611+
Thread.yield();
612+
}
613+
}
614+
}
615+
}
616+
}
617+
```
618+
592619
## FAQ
593620

594621
### start() 和 run() 有什么区别?可以直接调用 Thread 类的 run() 方法么?
@@ -661,7 +688,7 @@ Java 的每个对象中都有一个锁(monitor,也可以成为监视器) 并
661688
* [Java 并发编程实战](https://item.jd.com/10922250.html)
662689
* [Java 并发编程的艺术](https://item.jd.com/11740734.html)
663690
* https://stackoverflow.com/questions/27406200/visualvm-thread-states
664-
* https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html
691+
* https://docs.oracle.com/javase/8/docs/api/index.html
665692
* https://www.journaldev.com/1037/java-thread-wait-notify-and-notifyall-example
666693
* http://www.importnew.com/14958.html
667694
* https://blog.csdn.net/xiangwanpeng/article/details/54972952

0 commit comments

Comments
 (0)