現代的計算機,大多遵守 馮諾依曼體系結構 (Von Neumann Architecture)


AMD Ryzen 7 580OU with Radeon Graphics
GHz 叫做 CPU 的主頻
這個(gè)數字越大,CPU 就算的越快,表示 1s 執行 32 億條指令
存儲器: 分為外存和內存, 用于存儲數據(使用二進(jìn)制方式存儲)
輸入設備: 用戶(hù)給計算機發(fā)號施令的設備.
輸出設備:計算機個(gè)用戶(hù)匯報結果的設備
針對存儲空間
- 硬盤(pán) > 內存 >> CPU
針對數據訪(fǎng)問(wèn)速度
- CPU >> 內存 > 硬盤(pán)
認識計算機的祖師爺 – 馮諾依曼
馮·諾依曼(John von Neumann,1903年12月28日-1957年2月8日), 美籍匈牙利數學(xué)家、計算機科學(xué)家、物理學(xué)家,是20世紀最重要的數學(xué)家之一。馮·諾依曼是布達佩斯大學(xué)數學(xué)博士,在現代計算機、博弈論、核武器和生化武器等領(lǐng)域內的科學(xué)全才之一,被后人稱(chēng)為 “現代計算機之父”, “博弈論之父”.
電子開(kāi)關(guān) —— 機械繼電器 (Mechanical Relay):
電磁繼電器:通過(guò)通電 / 不通電來(lái)切換開(kāi)關(guān)狀態(tài),得到 1 或者 0 這樣的數據

基于上述的 “電子開(kāi)關(guān)” 就能構造出基本的門(mén)電路,可以實(shí)現 1 位(bit) 的基本邏輯運算
最基礎的門(mén)電路,有三種:
非門(mén):可以對一個(gè) 0/1 進(jìn)行取反. 0-> 1
與門(mén):可以針對兩個(gè) 0/1 進(jìn)行與運算. 1 0 -> 0
或門(mén):可以針對兩個(gè) 0/1 進(jìn)行或運算. 1 0 -> 1
針對二進(jìn)制數據來(lái)進(jìn)行的.不是"邏輯與”,此處是按位與
借助上述的基礎門(mén)電路,能構造出一個(gè)更復雜的門(mén)電路:異或門(mén)
相同為0,相異為1。1 0 -> 1
基于上述的門(mén)電路,還可以搭建出一些運算器
半加器:是針對兩個(gè)比特位,進(jìn)行加法運算


基于上述的半加器和全加器,就可以構造出一個(gè)針對多個(gè) bit 位的數據進(jìn)行加法運算的加法器了

電子開(kāi)關(guān)=>基礎的門(mén)電路=>異或門(mén)電路=>半加器=>全加器=>8位加法器
有了加法器之后,就可以計算不只是加法,還能計算減法、乘法、除法都是通過(guò)這個(gè)加法器來(lái)進(jìn)行
CPU里面除了運算器之外,還有控制單元和寄存器(Register)
門(mén)電路 (電子開(kāi)關(guān))
CPU芯片來(lái)說(shuō),上面就集成了非常非常多的這些電子開(kāi)關(guān),一個(gè)CPU上面的電子開(kāi)關(guān)越多,就認為是計算能力就越強
CPU里面除了運算器之外,還有控制單元和寄存器
寄存器是CPU內部用來(lái)存儲數據的組件
訪(fǎng)問(wèn)速度:寄存器是內存的3-4個(gè)數量級
存儲空間:比內存小很多很多,現在的x64的cpu (64位的cpu),大概有幾十個(gè)寄存器,每個(gè)寄存器是8個(gè)字節,幾百個(gè)字節,
成本:CPU上面的這個(gè)寄存器,還是非常貴
持久化:掉電之后數據丟失
控制單元 CU(Control Unit):
協(xié)調CPU來(lái)去進(jìn)行工作
控制單元最主要的工作,能夠去執行指令
后面進(jìn)行詳細的論述
指令和編程密切相關(guān)。
編程語(yǔ)言,大概分成三類(lèi):
1、機器語(yǔ)言
通過(guò)二進(jìn)制的數字,來(lái)表示的不同的操作
不同的CPU (哪怕是同一個(gè)廠(chǎng)商,但是不同型號的CPU),所支持的機器語(yǔ)言(指令)也可能存在差別
2、匯編語(yǔ)言
一個(gè)CPU到底支持哪些指令,生產(chǎn)廠(chǎng)商,會(huì )提供一個(gè)**"芯片手冊”** 詳細介紹CPU都支持哪些指令,每個(gè)指令都是干啥的
匯編語(yǔ)言和機器語(yǔ)言是一對一的關(guān)系 (完全等價(jià))
不同的CPU支持的機器指令不一樣,不同的CPU上面跑的匯編也不一樣
學(xué)校的大部分的匯編語(yǔ)言都是針對一款上古神 U,Intel 8086 CPU
3、高級語(yǔ)言
(C,Java,JS)
指令是如何執行的?
自己構造出一個(gè)最簡(jiǎn)單的芯片手冊:

假設CPU上有兩個(gè)寄存器
A 00
B 01
0010 1010
這個(gè)操作的意思,就是把1010 內存地址上的數據給讀取到A寄存器中
0001 1111
這個(gè)操作的意思,就是把 1111內存地址上的數據讀到寄存器 B 中
0100 1000
這個(gè)操作的意思,就是把 A寄存器的值,存到 1000這個(gè)內存地址中
1000 0100
這個(gè)操作的意思,就是把 00寄存器和01寄存器的數值進(jìn)行相加,結果放到 00 寄存器里

CPU的工作流程:(通過(guò)CU控制單元來(lái)實(shí)現的)
- 從內存中讀取指令
- 解析指令
- 執行指令
咱們編寫(xiě)的程序,最終都會(huì )被編譯器給翻譯成 CPU 所能識別的機器語(yǔ)言指令,在運行程序的時(shí)候,操作系統把這樣的可執行程序加載到內存中,cpu 就一條一條指令的去進(jìn)行讀取,解析,和執行,如果再搭配上條件跳轉,此時(shí),就能實(shí)現條件語(yǔ)句和循環(huán)語(yǔ)句
操作系統是一組做計算機資源管理的軟件的統稱(chēng)。目前常見(jiàn)的操作系統有:Windows系列、Unix系列、
Linux系列、OSX系列、Android系列、iOS系列、鴻蒙等
操作系統是一個(gè)搞 "管理的軟件"
- 對下,要管理好各種硬件設備
- 對上,要給各種軟件提供穩定的運行環(huán)境
exe 可執行文件,都是靜靜的躺在硬盤(pán)上的,在你雙擊之前,這些文件不會(huì )對你的系統有任何影響
但是,一旦你雙擊執行這些 exe 文件,操作系統就會(huì )把這個(gè) exe 給加載到內存中,并且讓 CPU 開(kāi)始執行exe內部的一些指令 (exe里面就存了很多這個(gè)程序對應的二進(jìn)制指令)
這個(gè)時(shí)候,就已經(jīng)把 exe給執行起來(lái),開(kāi)始進(jìn)行了一些具體的工作
這些運行起來(lái)的可執行文件,稱(chēng)為 "進(jìn)程"
這些都是機器上運行的進(jìn)程:

jvm)
- 先描述一個(gè)進(jìn)程 (明確出一個(gè)進(jìn)程上面的一些相關(guān)屬性)
- 再組織若干個(gè)進(jìn)程 (使用一些數據結構,把很多描述進(jìn)程的信息給放到一起,方便進(jìn)行增刪改查)
描述進(jìn)程:操作系統里面主要都是通過(guò) C/C++來(lái)實(shí)現的,此處的描述其實(shí)就是用的C語(yǔ)言中的 “結構體” (也就和Java的類(lèi)差不多)
**操作系統中描述進(jìn)程的這個(gè)結構體, "PCB" (process control block),進(jìn)程控制塊,這個(gè)東西不是硬件中的那個(gè)PCB板
組織進(jìn)程:典型的實(shí)現,就是使用雙向鏈表來(lái)把每個(gè)進(jìn)程的PCB給串起來(lái)
操作系統的種類(lèi)是很多的,內部的實(shí)現也是各有不同,咱們此處所討論的情況,是以Linux這個(gè)系統為例,由于windows, mac 這樣的系統,不是開(kāi)源的,里面的情況我們并不知道
1、pid (進(jìn)程id)
進(jìn)程的身份標識,進(jìn)程的身份證號

exe,此時(shí)操作系統就會(huì )把這個(gè) exe 加載到內存中,變成進(jìn)程3、文件描述符表:
程序運行過(guò)程中,經(jīng)常要和文件打交道 (文件是在硬盤(pán)上的)
文件操作:打開(kāi)文件,讀/寫(xiě)文件,關(guān)閉文件
進(jìn)程每次打開(kāi)一個(gè)文件,就會(huì )在文件描述符表上多增加一項,(個(gè)文件描述符表就可以視為是一個(gè)數組,里面的每個(gè)元素,又是一個(gè)結構體,就對應一個(gè)文件的相關(guān)信息)
一個(gè)進(jìn)程只要一啟動(dòng),不管你代碼中是否寫(xiě)了打開(kāi) / 操作文件的代碼,都會(huì )默認的打開(kāi)三個(gè)文件 (系統自動(dòng)打開(kāi)的),標準輸入(System.in),準輸出(System.out) 標準錯誤(System.err)
要想能讓一個(gè)進(jìn)程正常工作,就需要給這個(gè)進(jìn)程分配一些系統資源:內存,硬盤(pán),CPU
這個(gè)文件描述符表的下標,就稱(chēng)為文件描述符
4、進(jìn)程調度:
5、并行和并發(fā):
并行和并發(fā)這兩件事, 只是在微觀(guān)上有區分
宏觀(guān)上咱們區分不了,微觀(guān)上這里的區分都是操作系統自行調度的結果
例如6個(gè)核心,同時(shí)跑20個(gè)任務(wù)
這20個(gè)任務(wù), 有些是并行的關(guān)系, 有些是并發(fā)的關(guān)系??赡苋蝿?wù)A和任務(wù)B,一會(huì )是并行, 一會(huì )是并發(fā)….都是微觀(guān)上操作系統在控制的,在宏觀(guān)上感知不到
正因為在宏觀(guān)上區分不了并行并發(fā), 我們在寫(xiě)代碼的時(shí)候也就不去具體區分這兩個(gè)詞實(shí)際上通常使用 “并發(fā)” 這個(gè)詞, 來(lái)代指并行+并發(fā)
咱們只是在研究操作系統進(jìn)程調度這個(gè)話(huà)題上的時(shí)候, 稍作區分但是其他場(chǎng)景上基本都是使用并發(fā)作為一個(gè)統稱(chēng)來(lái)代替的,并發(fā)編程
6、調度
所謂的調度就是 “時(shí)間管理”,
并發(fā)就是規劃時(shí)間表的過(guò)程,也就是“調度"的過(guò)程
7、狀態(tài)
狀態(tài)就描述了當前這個(gè)進(jìn)程接下來(lái)應該怎么調度
Linux中的進(jìn)程狀態(tài)還有很多其他的…
8、優(yōu)先級
先給誰(shuí)分配時(shí)間,后給誰(shuí)分配時(shí)間,以及給誰(shuí)分的多,給誰(shuí)分的少…
9、記賬信息
統計了每個(gè)進(jìn)程,都分別被執行了多久,分別都執行了哪些指令,分別都排隊等了多久了…
給進(jìn)程調度提供指導依據的
10、上下文
就表示了上次進(jìn)程被調度出 CPU 的時(shí)候,當時(shí)程序的執行狀態(tài)。下次進(jìn)程上CPU的時(shí)候,就可以恢復之前的狀態(tài),然后繼續往下執行
進(jìn)程被調度出CPU之前,要先把CPU中的所有的寄存器中的數據都給保存到內存中 (PCB的上下文字段中) ,相當于存檔了
下次進(jìn)程再被調度上CPU的時(shí)候,就可以從剛才的內存中恢復這些數據到寄存器中,相當于讀檔了
存檔+讀檔,存檔存儲的游戲信息,就稱(chēng)為 “上下文”
進(jìn)程的調度,其實(shí)就是操作系統在考慮CPU資源如何給各個(gè)進(jìn)程分配
那內存資源又是如何分配的呢?
11、虛擬地址空間:
由于操作系統上,同時(shí)運行著(zhù)很多個(gè)進(jìn)程,如果某個(gè)進(jìn)程,出現了bug 進(jìn)程崩潰了,是否會(huì )影響到其他進(jìn)程呢?
現代的操作系統 (windows, linux, mac… ) ,能夠做到這一點(diǎn),就是 “進(jìn)程的獨立性” 來(lái)保證的,就依仗了"虛擬地址空間"
例:如果某個(gè)居民核酸變成陽(yáng)性了,是否會(huì )影響到其他的居民呢?
一旦發(fā)現有人陽(yáng)性了,就需要立刻封樓封小區,否則就會(huì )導致其他人也被傳染,
這個(gè)情況就類(lèi)似于早期的操作系統,早期的操作系統,里面的進(jìn)程都是訪(fǎng)問(wèn)同一個(gè)內存的地址空間。如果某個(gè)進(jìn)程出現 bug,把某個(gè)內存的數據給寫(xiě)錯了,就可能引起其他進(jìn)程的崩潰
解決方案,就是把這個(gè)院子,給劃分出很多的道路
這些道路之間彼此隔離開(kāi),每個(gè)人走各自的道理,這個(gè)時(shí)候就沒(méi)事了,此時(shí)即使有人確診,也影響不到別人了,
如果把進(jìn)程按照虛擬地址空間的方式給劃分出了很多份,這個(gè)時(shí)候不是每一份就只剩一點(diǎn)了嘛?? 雖然你的系統有百八十個(gè)進(jìn)程,但是實(shí)際上從微觀(guān)上看,同時(shí)執行的進(jìn)程,就6個(gè)!!
每個(gè)進(jìn)程能夠撈著(zhù)的內存還是挺多的,而且另一方面,也不是所有的進(jìn)程都用那么多的內存,有的進(jìn)程 (一個(gè)3A游戲,吃幾個(gè)G),大多數的進(jìn)程也就只占幾M即可
12、進(jìn)程間通信
進(jìn)程之間現在通過(guò)虛擬地址空間,已經(jīng)各自隔離開(kāi)了,但是在實(shí)際工作中,進(jìn)程之間有的時(shí)候還是需要相互交互的。
例:某業(yè)主A問(wèn):兄弟們,誰(shuí)家有土豆,借我兩個(gè)
業(yè)主B回答:我有土豆,我給你
設定一個(gè)公共空間,這個(gè)空間是任何居民都可以來(lái)訪(fǎng)問(wèn)的,
讓B先把土豆放到公共空間中,進(jìn)行消毒,再讓A來(lái)把這個(gè)公共空間的土豆給取走,彼此就不容易發(fā)生傳染
類(lèi)似的,咱們的兩個(gè)進(jìn)程之間,也是隔離開(kāi)的,也是不能直接交互的,操作系統也是提供了類(lèi)似的 "公共空間”,
進(jìn)程 A 就可以把數據見(jiàn)放到公共空間上,進(jìn)程B再取走
進(jìn)程間通信:
操作系統中,提供的 “公共空間” 有很多種,并且各有特點(diǎn),有的存儲空間大,有的小,有的速度快,有的慢.….
操作系統中提供了多種這樣的進(jìn)程間通信機制,(有些機制是屬于歷史遺留的,已經(jīng)不適合于現代的程序開(kāi)發(fā))
現在最主要使用的進(jìn)程間通信方式兩種:
1.文件操作
2.網(wǎng)絡(luò )操作 (socket)
總結:
為啥要有進(jìn)程?因為我們的系統支持多任務(wù)了,程序猿也就需要 “并發(fā)編程”
通過(guò)多進(jìn)程,是完全可以實(shí)現并發(fā)編程的,但是有點(diǎn)小問(wèn)題:
如果需要頻繁的創(chuàng )建而 / 銷(xiāo)毀進(jìn)程,這個(gè)事情成本是比較高的,如果需要頻繁的調度進(jìn)程,這個(gè)事情成本也是比較高的:
對于資源的申請和放,本身就是一個(gè)比較低效的操作,
創(chuàng )建進(jìn)程就得分配資源:
1)內存
2)文件
銷(xiāo)毀進(jìn)程也得釋放資源
1)內存
2)文件
如何解決這個(gè)問(wèn)題?思路有兩個(gè):
Linux 上也把線(xiàn)程稱(chēng)為輕量級進(jìn)程(LWP light weight process)可以把進(jìn)程比作一個(gè)工廠(chǎng),假設這個(gè)工廠(chǎng)有一些生產(chǎn)任務(wù),例如要生產(chǎn) 1w 部手機
要想提高生產(chǎn)效率:
1). 搞兩個(gè)工廠(chǎng),一個(gè)生產(chǎn) 5k (多創(chuàng )建了一個(gè)進(jìn)程)
2). 還是一個(gè)工廠(chǎng),在一個(gè)工廠(chǎng)里多加一個(gè)生產(chǎn)線(xiàn),兩個(gè)生產(chǎn)線(xiàn)并行生產(chǎn),一個(gè)生產(chǎn)線(xiàn)生產(chǎn)5k,(多創(chuàng )建了一個(gè)線(xiàn)程)
最終生產(chǎn)1w個(gè)手機,花的時(shí)間差不多,但是這里的成本就不一樣了
多加一些線(xiàn)程,是不是效率就會(huì )進(jìn)一步提高呢?一般來(lái)說(shuō)是會(huì ),但是也不一定
如果線(xiàn)程多了,這些線(xiàn)程可能要競爭同一個(gè)資源,這個(gè)時(shí)候,整體的速度就收到了限制,整體硬件資源是有限的
總結進(jìn)程與線(xiàn)程的區別:
并發(fā)編程 這樣的場(chǎng)景CPU上調度執行,線(xiàn)程是操作系統調度執行的基本單位,(前面講的時(shí)間管理,當時(shí)咱們是調度的進(jìn)程,但是更準確的說(shuō),其實(shí)是調度的線(xiàn)程)
Java這個(gè)生態(tài)中更常使用的并發(fā)編程方式,是多線(xiàn)程
其他的語(yǔ)言,主打的并發(fā)變成又不一樣:
go,主要是通過(guò)多協(xié)程的方式實(shí)現并發(fā).
erlang,這個(gè)是通過(guò) actor 模型實(shí)現并發(fā).
JS,是通過(guò)定時(shí)器+事件回調的方式實(shí)現并發(fā).……
多線(xiàn)程仍然是最主流最常見(jiàn)的一種并發(fā)編程的方式
Java 的線(xiàn)程 和 操作系統線(xiàn)程 的關(guān)系:
Thread 類(lèi),來(lái)表示 / 操作線(xiàn)程,Thread 類(lèi)可以視為 Java 標準庫提供的 API, 對操作系統提供的 API 進(jìn)行了進(jìn)一步的抽象和封裝Thread實(shí)例,其實(shí)和操作系統中的線(xiàn)程是一 一對應的關(guān)系,操作系統提供了一組關(guān)于線(xiàn)程的API(C語(yǔ)言風(fēng)格),Java對于這組API進(jìn)一步封裝了,就成了Thread 類(lèi)Thread類(lèi)的基本用法
通過(guò) Thread 類(lèi)創(chuàng )建線(xiàn)程,寫(xiě)法有很多種
其中最簡(jiǎn)單的做法,創(chuàng )建子類(lèi),繼承自Thread,并且重寫(xiě) run 方法
package thread;
class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello thread!");;
}
}
public class demo1 {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
}
}
run 方法描述了,這個(gè)線(xiàn)程內部要執行哪些代碼,每個(gè)線(xiàn)程都是并發(fā)執行的 (各自執行各自的代碼),因此就需要告知這個(gè)線(xiàn)程,你執行的代碼是什么,
run 方法中的邏輯,是在新創(chuàng )建出來(lái)的線(xiàn)程中,被執行的代碼
并不是我一定義這個(gè)類(lèi),一寫(xiě)run方法,線(xiàn)程就創(chuàng )建出來(lái),相當于我把活安排出來(lái)了,但是同學(xué)們還沒(méi)開(kāi)始干呢
需要調用這里的 start 方法,才是真正的在系統中創(chuàng )建了線(xiàn)程,才是真正開(kāi)始執行上面的 run 操作,在調用 start 之前,系統中是沒(méi)有創(chuàng )建出線(xiàn)程的
如果在一個(gè)循環(huán)中不加任何限制,這個(gè)循環(huán)轉的速度非常非???,導致打印的東西太多了,根本看不過(guò)來(lái)了,就可以加上一個(gè) sleep 操作,來(lái)強制讓這個(gè)線(xiàn)程休眠一段時(shí)間
這個(gè)休眠操作,就是強制地讓線(xiàn)程進(jìn)入阻塞狀態(tài),單位是 ms,就是1s 之內這個(gè)線(xiàn)程不會(huì )到 cpu 上執行
public void run() {
while (true) {
System.out.println("hello thread!");
Thread.sleep(1000);
}
}
這是多線(xiàn)程編程中最常見(jiàn)的一個(gè)異常,線(xiàn)程被強制的中斷了,用 try catch 處理

在一個(gè)進(jìn)程中,至少會(huì )有一個(gè)線(xiàn)程,
在一個(gè) java進(jìn)程中,也是至少會(huì )有一個(gè)調用 main 方法的線(xiàn)程 (這個(gè)線(xiàn)程不是你手動(dòng)搞出來(lái)的)
自己創(chuàng )建的 t 線(xiàn)程 和 自動(dòng)創(chuàng )建的 main 線(xiàn)程,就是并發(fā)執行的關(guān)系 (宏觀(guān)上看起來(lái)是同時(shí)執行)
此處的并發(fā) = 并行 + 并發(fā)
宏觀(guān)上是區分不了并行和并發(fā)的,都取決于系統內部的調度
package thread;
// Thread是在java.lang 里的,java.lang 里的類(lèi)都不需要手動(dòng)導入,類(lèi)似的還有String
class MyThread2 extends Thread {
@Override
public void run() {
while (true) {
System.out.println("hello thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class demo2 {
public static void main(String[] args) {
Thread t = new MyThread2();
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
運行打?。?/strong>
/* hello main
hello thread!
hello thread!
hello main
hello main
hello thread!
hello thread!
hello main */
現在兩個(gè)線(xiàn)程,都是打印一條,就休眠 1s
當1s 時(shí)間到了之后,系統先喚醒誰(shuí)呢?
看起來(lái)這個(gè)順序不是完全確定 (隨機的)
每一輪,1s 時(shí)間到了之后,到底是先喚醒 main 還是 thread,這是不確定的 (隨機的)
操作系統來(lái)說(shuō),內部對于線(xiàn)程之間的調度順序,在宏觀(guān)上可以認為是隨機的 (搶占式執行)
這個(gè)隨機性,會(huì )給多線(xiàn)程編程帶來(lái)很多其他的麻煩
寫(xiě)法一: 創(chuàng )建子類(lèi),繼承自 Thread
寫(xiě)法二: 創(chuàng )建一個(gè)類(lèi),實(shí)現 Runnable 接口,再創(chuàng )建 Runnable實(shí)例傳給Thread 實(shí)例
通過(guò) Runnable 來(lái)描述任務(wù)的內容
進(jìn)—步的再把描述好的任務(wù)交給Thread 實(shí)例
package thread;
// Runnable 就是在描述一個(gè)任務(wù)
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("hello");
}
}
public class demo3 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
}
}
寫(xiě)法三 / 寫(xiě)法四: 就是上面兩個(gè)寫(xiě)法的翻版,使用了匿名內部類(lèi)
創(chuàng )建了一個(gè)匿名內部類(lèi),繼承自 Thread 類(lèi),同時(shí)重寫(xiě)run方法,同時(shí)再new出這個(gè)匿名內部類(lèi)的實(shí)例
package thread;
public class demo4 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
System.out.println("hello thread!");
}
};
t.start();
}
}
new 的是Runnable,針對這個(gè)創(chuàng )建的匿名內部類(lèi),同時(shí)new 出的 Runnable` 實(shí)例傳給 Thread 的構造方法
package thread;
public class demo5 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello thread!");
}
});
t.start();
}
}
通常認為Runnable 這種寫(xiě)法更好一點(diǎn),能夠做到讓線(xiàn)程和線(xiàn)程執行的任務(wù),更好的進(jìn)行解耦
寫(xiě)代碼一般希望,高內聚,低耦合
Runnable 單純的只是描述了一個(gè)任務(wù),至于這個(gè)任務(wù)是要通過(guò)一個(gè)進(jìn)程來(lái)執行,還是線(xiàn)程來(lái)執行,還是線(xiàn)程池來(lái)執行,還是協(xié)程來(lái)執行,Runnable 本身并不關(guān)心,Runnable 里面的代碼也不關(guān)心
第五種寫(xiě)法: 相當于是第四種寫(xiě)法的延伸,使用 lambda 表達式,是使用lambda 代替了 Runnable 而已
package thread;
public class demo6 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("hello thread!");
});
t.start();
}
}
多線(xiàn)程能夠提高任務(wù)完成的效率
測試:有兩個(gè)整數變量,分別要對這倆變量自增10億次,分別使用一個(gè)線(xiàn)程,和兩個(gè)線(xiàn)程
此處不能直接這么記錄結束時(shí)間,別忘了,現 在這個(gè)求時(shí)間戳的代碼是在 main 線(xiàn)程中
main 和t1 ,t2 之間是并發(fā)執行的關(guān)系,此處t1和t2 還沒(méi)執行完呢,這里就開(kāi)始記錄結束時(shí)間了,這顯然是不準確的
正確做法應該是讓main線(xiàn)程等待 t1和 t2 跑完了,再來(lái)記錄結束時(shí)間
join 效果就是等待線(xiàn)程結束,t1.join就是讓main 線(xiàn)程等待t1 結束,t2.join讓 main 線(xiàn)程等待 t2結束
package thread;
public class demo7 {
private static final long count = 10_0000_0000;
public static void serial() {
long begin = System.currentTimeMillis();
long a = 0;
for (int i = 0; i < count; i++) {
a++;
}
long b = 0;
for (int i = 0; i < count; i++) {
b++;
}
long end = System.currentTimeMillis();
System.out.println("消耗時(shí)間: " + (end- begin) + "ms");
}
public static void concurrency() throws InterruptedException {
long begin = System.currentTimeMillis();
Thread t1 = new Thread(() -> {
long a = 0;
for (int i = 0; i < count; i++) {
a++;
}
});
t1.start();
Thread t2 = new Thread(() -> {
long b = 0;
for (int i = 0; i < count; i++) {
b++;
}
});
t2.start();
t1.join(); // 讓 main 線(xiàn)程等待 t1 結束
t2.join(); // 讓 main 線(xiàn)程等待 t2 結束
long end = System.currentTimeMillis();
System.out.println("消耗時(shí)間: " + (end- begin) + "ms");
}
public static void main(String[] args) throws InterruptedException {
// serial();
concurrency();
}
}
串行執行的時(shí)候,時(shí)間大概是600多ms (平均650左右)
兩個(gè)線(xiàn)程并發(fā)執行,時(shí)間大概是400多ms (平均450左右)
提升了接近50%
并不是說(shuō),一個(gè)線(xiàn)程600多ms,兩個(gè)線(xiàn)程就是300多ms
這倆線(xiàn)程在底層到底是并行執行,還是并發(fā)執行,不確定,真正并行執行的時(shí)候,效率才會(huì )有顯著(zhù)提升
多線(xiàn)程特別適合于那種CPU密集型的程序,程序要進(jìn)行大量的計算,使用多線(xiàn)程就可以更充分的利用CPU的多核資源
| 方法 | 說(shuō)明 |
|---|---|
| Thread() | 創(chuàng )建線(xiàn)程對象 |
| Thread(Runnable target) | 使用 Runnable 對象創(chuàng )建線(xiàn)程對象 |
| Thread(String name) | 創(chuàng )建線(xiàn)程對象,并命名 |
| Thread(Runnable target, String name) | 使用 Runnable 對象創(chuàng )建線(xiàn)程對象,并命名 |
| 【了解】Thread(ThreadGroup group, Runnable target) | 線(xiàn)程可以被用來(lái)分組管理,分好的組即為線(xiàn)程組,這 個(gè)目前我們了解即可 |
Thread(String name):這個(gè)東西是給線(xiàn)程 (thread對象) 起一個(gè)名字,起一個(gè)啥樣的名字,不影響線(xiàn)程本身的執行
僅僅只是影響到程序猿調試,可以借助一些工具看到每個(gè)線(xiàn)程以及名字,很容易在調試中對線(xiàn)程做出區分
可以使用 jconsole 來(lái)觀(guān)察線(xiàn)程的名字,jconsole是jdk自帶的一個(gè)調試工具

jconsole 這里能夠羅列出你系統上的java進(jìn)程(其他進(jìn)程不行)



| 屬性 | 獲取方法 |
|---|---|
| ID | getId() |
| 名稱(chēng) | getName() |
| 狀態(tài) | getState() |
| 優(yōu)先級 | getPriority() |
| 是否后臺線(xiàn)程 | isDaemon() |
| 是否存活 | isAlive() |
| 是否被中斷 | isInterrupted( |
是否后臺線(xiàn)程:isDaemon()
如果線(xiàn)程是后臺線(xiàn)程,不影響進(jìn)程退出
如果線(xiàn)程不是后臺線(xiàn)程 (前臺線(xiàn)程),就會(huì )影響到進(jìn)程退出
創(chuàng )建的 t1 和 t2 默認都是前臺的線(xiàn)程
即使 main 方法執行完畢,進(jìn)程也不能退出,得等 t1 和 t2 都執行完,整個(gè)進(jìn)程才能退出!
如果 t1 和 t2 是后臺線(xiàn)程,此時(shí)如果 main 執行完畢,整個(gè)進(jìn)程就直接退出,t1 和 t2 就被強行終止了
是否存活: isAlive()
操作系統中對應的線(xiàn)程是否正在運行
Thread t 對象的生命周期和內核中對應的線(xiàn)程,生命周期并不完全一致
創(chuàng )建出t對象之后,在調用 start 之前,系統中是沒(méi)有對應線(xiàn)程的
在run方法執行完了之后,系統中的線(xiàn)程就銷(xiāo)毀了,但是t這個(gè)對象可能還存在,通過(guò) isAlive就能判定當前系統的線(xiàn)程的運行情況
如果調用 start之后,run執行完之前,isAlive 就是返回true 。如果調用start 之前,run執行完之后,isAlive 就返回 false
ID是線(xiàn)程的唯一標識,不同線(xiàn)程不會(huì )重復
名稱(chēng)是各種調試工具用到
優(yōu)先級高的線(xiàn)程理論上來(lái)說(shuō)更容易被調度到
關(guān)于后臺線(xiàn)程,需要記住一點(diǎn):JVM會(huì )在一個(gè)進(jìn)程的所有非后臺線(xiàn)程結束后,才會(huì )結束運行。
是否存活,即簡(jiǎn)單的理解,為 run 方法是否運行結束了
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": 我還活著(zhù)");
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我即將死去");
});
System.out.println(Thread.currentThread().getName()
+ ": ID: " + thread.getId());
System.out.println(Thread.currentThread().getName()
+ ": 名稱(chēng): " + thread.getName());
System.out.println(Thread.currentThread().getName()
+ ": 狀態(tài): " + thread.getState());
System.out.println(Thread.currentThread().getName()
+ ": 優(yōu)先級: " + thread.getPriority());
System.out.println(Thread.currentThread().getName()
+ ": 后臺線(xiàn)程: " + thread.isDaemon());
System.out.println(Thread.currentThread().getName()
+ ": 活著(zhù): " + thread.isAlive());
System.out.println(Thread.currentThread().getName()
+ ": 被中斷: " + thread.isInterrupted());
thread.start();
while (thread.isAlive()) {}
System.out.println(Thread.currentThread().getName()
+ ": 狀態(tài): " + thread.getState());
}
}
start() 決定了系統中是不是真的創(chuàng )建出線(xiàn)程
start 和 run 的區別:
run()單純的只是一個(gè)普通的方法,描述了任務(wù)的內容start()則是一個(gè)特殊的方法,內部會(huì )在系統中創(chuàng )建線(xiàn)程
package thread;
public class demo1 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
// t.run();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
用 start() 是并發(fā)執行,而 run()循環(huán)打印 hello thread
run方法只是一個(gè)普通的方法,你在main線(xiàn)程里調用run,其實(shí)并沒(méi)有創(chuàng )建新的線(xiàn)程,這個(gè)循環(huán)仍然是在 main 線(xiàn)程中執行的
既然是在一個(gè)線(xiàn)程中執行,代碼就得從前到后的按順序運行,運行第一個(gè)循環(huán),再運行第二個(gè)循環(huán)
for (int i = 0; i < 5; i++) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
如果改成循環(huán)五次,打印五個(gè) hello thread,讓后打印 hello main
中斷線(xiàn)程:讓一個(gè)線(xiàn)程停下來(lái)
線(xiàn)程停下來(lái)的關(guān)鍵,是要讓線(xiàn)程對應的 run方法執行完
還有一個(gè)特殊的是 main 這個(gè)線(xiàn)程,對于main 來(lái)說(shuō),得是main方法執行完,線(xiàn)程就完了)
如何中斷線(xiàn)程:
1、標志位
可以手動(dòng)的設置一個(gè)標志位 (自己創(chuàng )建的變量,boolean),來(lái)控制線(xiàn)程是否要執行結束
package thread;
public class demo2 {
private static boolean isQuit = false;
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!isQuit) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
// 只要把這個(gè) isQuit 設為true,這個(gè)循環(huán)就退出了,進(jìn)一步的 run 就執行完了,再進(jìn)一步就是線(xiàn)程執行結束了
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isQuit = true;
System.out.println("終止 t 線(xiàn)程!");
}
}
運行輸出:
hello thread
hello thread
hello thread
hello thread
hello thread
終止 t 線(xiàn)程!
在其他線(xiàn)程中控制這個(gè)標志位,就能影響到這個(gè)線(xiàn)程的結束
此處因為,多個(gè)線(xiàn)程共用同一個(gè)虛擬地址空間,因此,main 線(xiàn)程修改的 isQuit 和 t 線(xiàn)程判定的 isQuit,是同一個(gè)值
2、interrupted()
但是,isQuit 并不嚴謹,更好的做法,使用 Thread 中內置的一個(gè)標志位來(lái)進(jìn)行判定
Thread.interrupted() 這是一個(gè)靜態(tài)的方法
Thread.currentThread().isInterrupted() 這是實(shí)例方法,其中 currentThread 能夠獲取到當前線(xiàn)程的實(shí)例
package thread;
public class demo3 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 在主線(xiàn)程中,調用 interrupt 方法,中斷這個(gè)線(xiàn)程
// 讓 t 線(xiàn)程被中斷
t.interrupt();
}
}
運行此代碼,打印五次 hello thread 后,出現異常,然后繼續打印 hello thread
調用 t.interrupt() 這個(gè)方法,可能產(chǎn)生兩種情況:
1). 如果 t 線(xiàn)程是處在就緒狀態(tài),就是設置線(xiàn)程的標志位為 true
2). 如果 t 線(xiàn)程處在阻塞狀態(tài) (sleep 休眠了),就會(huì )觸發(fā)一個(gè) InterruptException
這個(gè)代碼絕大部分情況,都是在休眠狀態(tài)阻塞
此處的中斷,是希望能夠立即產(chǎn)生效果的
如果線(xiàn)程已經(jīng)是阻塞狀態(tài)下,此時(shí)設置標志位就不能起到及時(shí)喚醒的效果
調用這個(gè) interrupt 方法,就會(huì )讓 sleep 觸發(fā)一個(gè)異常,從而導致線(xiàn)程從阻塞狀態(tài)被喚醒
當下的代碼,一旦觸發(fā)了異常之后,就進(jìn)入了catch 語(yǔ)句,在catch 中,就單純的只是打了一個(gè)日志
printStackTrace 是打印當前出現異常位置的代碼調用棧,打完日志之后,就直接繼續運行
解決方法:
package thread;
public class demo3 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
// 當前觸發(fā)異常后,立即退出循環(huán)
System.out.println("這個(gè)是收尾工作");
break;
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 在主線(xiàn)程中,調用 interrupt 方法,中斷這個(gè)線(xiàn)程
// 讓 t 線(xiàn)程被中斷
t.interrupt();
}
}
運行結果:
hello thread
hello thread
hello thread
hello thread
hello thread
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.demo3.lambda$main$0(demo3.java:9)
at java.lang.Thread.run(Thread.java:748)
這個(gè)是收尾工作
推薦的做法:
咱們一個(gè)代碼中的線(xiàn)程有很多個(gè),隨時(shí)哪個(gè)線(xiàn)程都可能會(huì )終止
Thread.interrupted() 這個(gè)方法判定的標志位是Thread的static成員,一個(gè)程序中只有一個(gè)標志位
Thread.currentThread().isInterrupted()這個(gè)方法判定的標志位是 Thread的普通成員,每個(gè)示例都有自己的標志位,一般就無(wú)腦使用這個(gè)方法即可
| 方法 | 說(shuō)明 |
|---|---|
| public void interrupt() | 中斷對象關(guān)聯(lián)的線(xiàn)程,如果線(xiàn)程正在阻塞,則以異常方式通知, 否則設置標志位 |
| public static boolean interrupted() | 判斷當前線(xiàn)程的中斷標志位是否設置,調用后清除標志位 |
| public boolean isInterrupted() | 判斷對象關(guān)聯(lián)的線(xiàn)程的標志位是否設置,調用后不清除標志位 |
多個(gè)線(xiàn)程之間,調度順序是不確定的,線(xiàn)程之間的執行是按照調度器來(lái)安排的,這個(gè)過(guò)程可以視為是 “無(wú)序,隨機”,這樣不太好,有些時(shí)候,我們需要能夠控制線(xiàn)程之間的順序
線(xiàn)程等待,就是其中一種控制線(xiàn)程執行順序的手段
此處的線(xiàn)程等待,主要是控制線(xiàn)程結束的先后順序
join():調用 join 的時(shí)候,哪個(gè)線(xiàn)程調用的 join ,哪個(gè)線(xiàn)程就會(huì )阻塞等待,要等到對應的線(xiàn)程執行完畢為止 (對應線(xiàn)程的 run 執行完)
package thread;
public class demo4 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
// 在主線(xiàn)程中,使用等待操作,等 t 線(xiàn)程執行結束
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
首先,調用這個(gè)方法的線(xiàn)程是 main 線(xiàn)程,針對t這個(gè)線(xiàn)程對象調用的,此時(shí)就是讓 main 等待t
調用 join 之后,main 線(xiàn)程就會(huì )進(jìn)入阻塞狀態(tài) (暫時(shí)無(wú)法在cpu上執行)
代碼執行到 join` 這一行,就暫時(shí)停下了,不繼續往下執行了
那么join什么時(shí)候能繼續往下走,恢復成就緒狀態(tài)呢?
就是等到 t 線(xiàn)程執行完畢 ( t 的 run方法跑完了)
通過(guò)線(xiàn)程等待,就是在**控制讓** t先結束,main 后結束,一定程度上的干預了這兩個(gè)線(xiàn)程的執行順序
這是代碼中控制的先后順序,就像剛才寫(xiě)的自增 100 億次這個(gè)代碼,計時(shí)操作就是要在計算線(xiàn)程執行完之后再執行
但是 join 操作默認情況下,是死等,不見(jiàn)不散,這不合理
join 提供了另外一個(gè)版本,就是可以設置等待時(shí)間,最長(cháng)等待多久,如果等不到,就不等了
try {
t.join(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
進(jìn)入 join 也會(huì )產(chǎn)生阻塞,這個(gè)阻塞不會(huì )一直持續下去,如果 10s 之內,t線(xiàn)程結束了,此時(shí) join直接返回
如果10s之后,t 仍然不結束, 此時(shí)join 也就直接返回
日常開(kāi)發(fā)中涉及到的一些 "等待” 相關(guān)的操作,一般都不會(huì )是死等,而是會(huì )有這樣的 "超時(shí)時(shí)間"
Thread.currentThread() 就能夠獲取到當前線(xiàn)程的引用 (Thread 實(shí)例的引用),哪個(gè)線(xiàn)程調用的這個(gè)currentThread,就獲取到的是哪個(gè)線(xiàn)程的實(shí)例
package thread;
public class demo5 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()); // Thread-0
}
};
t.start();
// 在 main 線(xiàn)程中調用的,拿到的就是 main 這個(gè)線(xiàn)程的實(shí)例
System.out.println(Thread.currentThread().getName()); // main
}
}
this.getName() :對于這個(gè)代碼來(lái),是通過(guò)繼承 Thread 的方式來(lái)創(chuàng )建線(xiàn)程
此時(shí)在 run 方法中,直接通過(guò) this,拿到的就是當前 Thread 的實(shí)例
Thread t = new Thread(){
@Override
public void run() {
System.out.println(this.getName());
}
};
t.start();
此處的 this 不是指向 Thread 類(lèi)型了,而是指向 Runnable,而 Runnable 只是一個(gè)單純的任務(wù),沒(méi)有 name 屬性的
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(this.getName()); // err
}
});
t.start();
要想拿到線(xiàn)程的名字,只能通過(guò) Thread.currentThread()
lambda 表達式效果同 Runnable
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
t.start();
sleep 所謂的休眠到底是在干啥?
進(jìn)程:PCB+雙向鏈表,這個(gè)說(shuō)法是針對只有一個(gè)線(xiàn)程的進(jìn)程是如此的
如果是一個(gè)進(jìn)程有多個(gè)線(xiàn)程,此時(shí)每個(gè)線(xiàn)程都有一個(gè)PCB,一個(gè)進(jìn)程對應的就是一組PCB了
PCB 上有一個(gè)字段tgroupld,這個(gè)id其實(shí)就相當于進(jìn)程的id,同一個(gè)進(jìn)程中的若干個(gè)線(xiàn)程的 tgroupld 是相同的
process control block
進(jìn)程控制塊 和 線(xiàn)程有啥關(guān)系?其實(shí) Linux內核不區分進(jìn)程和線(xiàn)程
進(jìn)程線(xiàn)程是程序猿寫(xiě)應用程序代碼,搞出來(lái)的詞,實(shí)際上 Linux內核只認PCB !
在內核里 Linux 把線(xiàn)程稱(chēng)為輕量級進(jìn)程

sleep 方法,這個(gè) PCB 就會(huì )進(jìn)入到阻塞隊列
操作系統調度線(xiàn)程的時(shí)候,就只是從就緒隊列中挑選合適的 PCB 到 CPU 上運行,阻塞隊列里的 PCB 就只能干等著(zhù),當睡眠時(shí)間到了,系統就會(huì )把剛才這個(gè) PCB 從阻塞隊列挪回到就緒隊列,以上情況都是在 Linux 系統
內核中的很多工作都依賴(lài)大量的數據結構,但凡是需要管理很多數據的程序,都大量的依賴(lài)數據結構
進(jìn)程有狀態(tài):就緒,阻塞
這里的狀態(tài)就決定了系統按照啥樣的態(tài)度來(lái)調度這個(gè)進(jìn)程,這里相當于是針對一個(gè)進(jìn)程中只有一個(gè)線(xiàn)程的情況
更常見(jiàn)的情況下,一個(gè)進(jìn)程中包含了多個(gè)線(xiàn)程,所謂的狀態(tài),其實(shí)是綁定在線(xiàn)程上
Linux 中,PCB 其實(shí)是和線(xiàn)程對應的,一個(gè)進(jìn)程對應著(zhù)一組 PCB
上面說(shuō)的 “就緒" 和 “阻塞” 都是針對系統層面上的線(xiàn)程的狀態(tài) (PCB)
在 Java 中Thread 類(lèi)中,對于線(xiàn)程的狀態(tài),又進(jìn)—步的細化了
1、 NEW:安排了工作, 還未開(kāi)始行動(dòng)
把 Thread 對象創(chuàng )建好了,但是還沒(méi)有調用 start
public class demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
});
System.out.println(t.getState()); // NEW
t.start();
}
}
2、 TERMINATED:工作完成了
操作系統中的線(xiàn)程已經(jīng)執行完畢,銷(xiāo)毀了但是 Thread 對象還在,獲取到的狀態(tài)
public class demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
});
t.start();
Thread.sleep(1000);
System.out.println(t.getState()); // TERMINATED
}
}
以上兩個(gè)狀態(tài)是 Java 內部搞出來(lái)的狀態(tài),就和操作系統中的 PCB 里的狀態(tài)就沒(méi)啥關(guān)系
3、 RUNNABLE:可工作的,又可以分成正在工作中和即將開(kāi)始工作
就緒狀態(tài),處于這個(gè)狀態(tài)的線(xiàn)程,就是在就緒隊列中,隨時(shí)可以被調度到 CPU 上
如果代碼中沒(méi)有進(jìn)行 sleep,也沒(méi)有進(jìn)行其他的可能導致阻塞的操作,代碼大概率是處在 Runnable 狀態(tài)的
public class demo7 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
// 這里什么都不能有
}
});
t.start();
Thread.sleep(1000);
System.out.println(t.getState()); // RUNNABLE
}
}
一直持續不斷的執行這里的循環(huán),隨時(shí)系統想調度它上cpu都是隨時(shí)可以的
4、TIMED_WAITING:這幾個(gè)都表示排隊等著(zhù)其他事情
代碼中調用了sleep,就會(huì )進(jìn)入到 TIMED_WAITIN,意思就是當前的線(xiàn)程在一定時(shí)間之內,是阻塞的狀態(tài)
public class demo7 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(1000);
System.out.println(t.getState()); // TIMED_WAITING
}
}
一定時(shí)間到了之后,阻塞狀態(tài)解除這種情況就是 TIMED_WAITING,也是屬于阻塞的狀態(tài)之一
5、BLOCKED:這幾個(gè)都表示排隊等著(zhù)其他事情
當前線(xiàn)程在等待鎖,導致了阻塞(阻塞狀態(tài)之一) --synchronized
6、WAITING:這幾個(gè)都表示排隊等著(zhù)其他事情
當前線(xiàn)程在等待喚醒,導致了阻塞(阻塞狀態(tài)之一) --wait
為啥要這么細分?這是非常有好處的:
開(kāi)發(fā)過(guò)程中經(jīng)常會(huì )遇到一種情況,程序 "卡死” 了
一些關(guān)鍵的線(xiàn)程,阻塞了
在分析卡死原因的時(shí)候,第一步就可以先來(lái)看看當前程序里的各種關(guān)鍵線(xiàn)程所處的狀態(tài)

聯(lián)系客服