# 線程生命周期(狀態)
當線程被創建并啟動以后,它既不是一啟動就進入了執行狀態,也不是一直處于執行狀態。 在線程的生命周期中,它要經過新建(New)、就緒(Runnable)、運行(Running)、阻塞 (Blocked)和死亡(Dead)5 種狀態。尤其是當線程啟動以后,它不可能一直"霸占"著 CPU 獨自 運行,所以 CPU 需要在多條線程之間切換,于是線程狀態也會多次在運行、阻塞之間切換
## 新建狀態(NEW)
當程序使用 new 關鍵字創建了一個線程之后,該線程就處于新建狀態,此時僅由 JVM 為其分配 內存,并初始化其成員變量的值
## 就緒狀態(RUNNABLE)
當線程對象調用了 start()方法之后,該線程處于就緒狀態。Java 虛擬機會為其創建方法調用棧和 程序計數器,等待調度運行。
## 運行狀態(RUNNING)
如果處于就緒狀態的線程獲得了 CPU,開始執行 run()方法的線程執行體,則該線程處于運行狀 態。
## 阻塞狀態(BLOCKED)
阻塞狀態是指線程因為某種原因放棄了 cpu 使用權,也即讓出了 cpu timeslice,暫時停止運行。 直到線程進入可運行(runnable)狀態,才有機會再次獲得 cpu timeslice 轉到運行(running)狀 態。阻塞的情況分三種:
### 等待阻塞(o.wait->等待對列)
運行(running)的線程執行 o.wait()方法,JVM 會把該線程放入等待隊列(waitting queue) 中。
### 同步阻塞(lock->鎖池)
運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則 JVM 會把該線 程放入鎖池(lock pool)中。
### 其他阻塞(sleep/join)
運行(running)的線程執行 Thread.sleep(long ms)或 t.join()方法,或者發出了 I/O 請求時, JVM 會把該線程置為阻塞狀態。當 sleep()狀態超時、join()等待線程終止或者超時、或者 I/O 處理完畢時,線程重新轉入可運行(runnable)狀態。
## 線程死亡(DEAD)
線程會以下面三種方式結束,結束后就是死亡狀態。
### 正常結束
1. run()或 call()方法執行完成,線程正常結束。
### 異常結束
2. 線程拋出一個未捕獲的 Exception 或 Error。
### 調用 stop
3. 直接調用該線程的 stop()方法來結束該線程—**該方法通常容易導致死鎖**,不推薦使用。

# 終止線程的四種方式
## 正常運行結束
程序運行結束,線程自動結束。
## 使用退出標志退出線程
一般 run()方法執行完,線程就會正常結束,然而,常常有些線程是伺服線程。它們需要長時間的 運行,只有在外部某些條件滿足的情況下,才能關閉這些線程。使用一個變量來控制循環,例如: 最直接的方法就是設一個 boolean 類型的標志,并通過設置這個標志為 true 或 false 來控制 while 循環是否退出,代碼示例:
```java
/**
* @program: ThreadDemo
* @description: 使用退出標志退出線程
* @author: hs96.cn@Gmail.com
* @create: 2020-08-27 15:10
*/
public class ThreadSafe extends Thread {
public volatile boolean exit = false;
public void run() {
while (!exit) {
//do something
}
}
public static void main(String[] args) {
ThreadSafe threadSafe = new ThreadSafe();
threadSafe.run();
}
}
```
定義了一個退出標志 exit,當 exit 為 true 時,while 循環退出,exit 的默認值為 false.在定義 exit 時,**使用了一個 Java 關鍵字 volatile**,這個關鍵字的目的是使 exit 同步,也就是說在同一時刻只 能由一個線程來修改 exit 的值。
## Interrupt 方法結束線程
使用 interrupt()方法來中斷線程有兩種情況:
### 線程處于阻塞狀態
如使用了 sleep,同步鎖的 wait,socket 中的 receiver,accept 等方法時, 會使線程處于阻塞狀態。當調用線程的 interrupt()方法時,會拋出 InterruptException 異常。 阻塞中的那個方法拋出這個異常,通過代碼捕獲該異常,然后 break 跳出循環狀態,從而讓 我們有機會結束這個線程的執行。通常很多人認為只要調用 interrupt 方法線程就會結束,實 際上是錯的, 一定要先捕獲 InterruptedException 異常之后通過 break 來跳出循環,才能正 常結束 run 方法。
### 線程未處于阻塞狀態
使用 isInterrupted()判斷線程的中斷標志來退出循環。當使用 interrupt()方法時,中斷標志就會置 true,和使用自定義的標志來控制循環是一樣的道理。
```java
public class ThreadSafe extends Thread {
public void run() {
while (!isInterrupted()){ //非阻塞過程中通過判斷中斷標志來退出
try{
Thread.sleep(5*1000);//阻塞過程捕獲中斷異常來退出
}catch(InterruptedException e){
e.printStackTrace();
break;//捕獲到異常之后,執行 break 跳出循環
}
}
}
}
```
## stop 方法終止線程(線程不安全)
程序中可以直接使用 thread.stop()來強行終止線程,但是 stop 方法是很危險的,就象突然關 閉計算機電源,而不是按正常程序關機一樣,可能會產生不可預料的結果,不安全主要是: thread.stop()調用之后,創建子線程的線程就會拋出 ThreadDeatherror 的錯誤,并且會釋放子 線程所持有的所有鎖。一般任何進行加鎖的代碼塊,都是為了保護數據的一致性,如果在調用 thread.stop()后導致了該線程所持有的所有鎖的突然釋放(不可控制),那么被保護數據就有可能呈 現不一致性,其他線程在使用這些被破壞的數據時,有可能導致一些很奇怪的應用程序錯誤。因 此,并不推薦使用 stop 方法來終止線程。
# start()源碼簡單剖析:
在lang包里的Thread.java找到start()方法可以看到,實際就是native的start0()。

當原生線程初始化完畢,就會調用 Java 線程的 run() 方法。

其實這種設計技巧是一種模板方法,下面來編寫一個模板方法來體會Thread的start()設計的思想:
## 其中的設計模式
```java
/**
* @program: ThreadDemo
* @description: 編寫一個模板方法來體會Thread的start()設計的思想
* @author: hs96.cn@Gmail.com
* @create: 2020-08-26
*/
public abstract class BaseTemplateMethod {
public static void main(String[] args) {
BaseTemplateMethod t1 = new BaseTemplateMethod() {
@Override
protected void wrapPrint(String message) {
System.out.println("*" + message + "*");
}
};
t1.print("Hello Thread");
BaseTemplateMethod t2 = new BaseTemplateMethod() {
@Override
protected void wrapPrint(String message) {
System.out.println("+" + message + "+");
}
};
t2.print("Hello Thread");
}
public final void print(String message) {
System.out.println("#####################");
wrapPrint(message);
System.out.println("#####################");
}
protected abstract void wrapPrint(String message);
}
```

wrapPrint就相當于start方法,print就相當于run方法。
- 微服務
- 服務器相關
- 操作系統
- 極客時間操作系統實戰筆記
- 01 程序的運行過程:從代碼到機器運行
- 02 幾行匯編幾行C:實現一個最簡單的內核
- 03 黑盒之中有什么:內核結構與設計
- Rust
- 入門:Rust開發一個簡單的web服務器
- Rust的引用和租借
- 函數與函數指針
- Rust中如何面向對象編程
- 構建單線程web服務器
- 在服務器中增加線程池提高吞吐
- Java
- 并發編程
- 并發基礎
- 1.創建并啟動線程
- 2.java線程生命周期以及start源碼剖析
- 3.采用多線程模擬銀行排隊叫號
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的應用分析
- 6.Daemon線程的創建以及使用場景分析
- 7.線程ID,優先級
- 8.Thread的join方法
- 9.Thread中斷Interrupt方法學習&采用優雅的方式結束線程生命周期
- 10.編寫ThreadService實現暴力結束線程
- 11.線程同步問題以及synchronized的引入
- 12.同步代碼塊以及同步方法之間的區別和關系
- 13.通過實驗分析This鎖和Class鎖的存在
- 14.多線程死鎖分析以及案例介紹
- 15.線程間通信快速入門,使用wait和notify進行線程間的數據通信
- 16.多Product多Consumer之間的通訊導致出現程序假死的原因分析
- 17.使用notifyAll完善多線程下的生產者消費者模型
- 18.wait和sleep的本質區別
- 19.完善數據采集程序
- 20.如何實現一個自己的顯式鎖Lock
- 21.addShutdownHook給你的程序注入鉤子
- 22.如何捕獲線程運行期間的異常
- 23.ThreadGroup API介紹
- 24.線程池原理與自定義線程池一
- 25.給線程池增加拒絕策略以及停止方法
- 26.給線程池增加自動擴充,閑時自動回收線程的功能
- JVM
- C&C++
- GDB調試工具筆記
- C&C++基礎
- 一個例子理解C語言數據類型的本質
- 字節順序-大小端模式
- Php
- Php源碼閱讀筆記
- Swoole相關
- Swoole基礎
- php的五種運行模式
- FPM模式的生命周期
- OSI網絡七層圖片速查
- IP/TCP/UPD/HTTP
- swoole源代碼編譯安裝
- 安全相關
- MySql
- Mysql基礎
- 1.事務與鎖
- 2.事務隔離級別與IO的關系
- 3.mysql鎖機制與結構
- 4.mysql結構與sql執行
- 5.mysql物理文件
- 6.mysql性能問題
- Docker&K8s
- Docker安裝java8
- Redis
- 分布式部署相關
- Redis的主從復制
- Redis的哨兵
- redis-Cluster分區方案&應用場景
- redis-Cluster哈希虛擬槽&簡單搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸縮調優
- 源碼閱讀筆記
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的東西
- 一次折騰了幾天的大華攝像頭調試經歷
- 搬磚實用代碼
- python讀取excel拼接sql
- mysql大批量插入數據四種方法
- composer好用的鏡像源
- ab
- 環境搭建與配置
- face_recognition本地調試筆記
- 虛擬機配置靜態ip
- Centos7 Init Shell
- 發布自己的Composer包
- git推送一直失敗怎么辦
- Beyond Compare過期解決辦法
- 我的Navicat for Mysql
- 小錯誤解決辦法
- CLoin報錯CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM無法連接到虛擬機
- Jetbrains相關
- IntelliJ IDEA 筆記
- CLoin的配置與使用
- PhpStormDocker環境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer