初識多執行緒
- 前言
- 一、作業系統
- 1.馮諾依曼體系結構
- 2.作業系統
- 二、行程
- 2.1作業系統管理行程
- 2.2行程的組成
- 2.3時間片
- 2.4并行和并發
- 2.5內核態和用戶態
- 2.6行程狀態
- 三、多執行緒
- 3.1執行緒是什么?
- 3.2行程和執行緒的區別
- 3.3Java實作多執行緒
- 3.4通過代碼演示多執行緒提高效率
前言
我們先簡單介紹作業系統來進一步了解多執行緒
一、作業系統
1.馮諾依曼體系結構
馮諾依曼體系結構是由CPU(運算器,控制器) 存盤器 輸入設備 和輸出設備組成
CPU(運算器,控制器): 算術運算和邏輯判斷
存盤器:主要功能是存盤設備 分為記憶體和外存 記憶體一般空間比較小,但是訪問速度快,記憶體空間大,但是訪問速度慢
輸入輸出設備輸入設備 鍵盤滑鼠 輸出設備 顯示幕,列印機等
簡單描述CPU是如何作業的:
CPU的核心功能就是執行一些指令,指令就是一些二進制的資料,用來表示一些特定的含義,例如我們寫的java代碼先被編譯器編譯為位元組碼之后被JVM翻譯為成一條條的CPU指令,最終在CPU上執行,而 C++代碼直接把源代碼就編譯成二進制的機器指令了,Java/C++ 代碼在編譯好之后就得到了一些二進制的機器指令,這些機器指令是保存在硬碟上的,當我們進行運行的時候,這時候作業系統先把這些指令從磁盤加載到記憶體中,再由CPU從記憶體中讀取指令進行執行
2.作業系統
作業系統是一個軟體,一個管理的軟體,管理硬體設備(CPU,記憶體,磁盤,滑鼠等)和軟體資源(行程,執行緒,內核中特殊的資源),作業系統是計算機最重要的軟體,
作業系統在其中運籌帷握,才能讓這些硬體和軟體能夠很好的搭配作業
當前主流的作業系統 PC端 Windows Linux Mac 移動端 IOS Android 鴻蒙
二、行程
行程(Process)作業系統中的核心概念,也和平時運行程式密切相關,行程是按照一定的流程(代碼中所撰寫的CPU指令)來具體執行一些計算作業,通俗來說:當運行某個程式的時候,作業系統就創建了一個對應的行程
行程的定義:
1)行程是程式的動態執行
2)行程是一個程式及其資料在處理機上順序執行時所發生的活動
3)行程是具有獨立功能的程式在其資料集合上運行的程序,他是系統調度和資源分配的一個獨立單位
我們可以通過任務管理器來查看當前電腦上由多少行程,
為什么引入行程:
因為現代作業系統,都需要支持“多任務”“并發” 執行,多任務并發執行能更充分利用多核資源,還有更充分利用CPU資源
1)系統支持多任務
2)更充分利用CPU資源
2.1作業系統管理行程
描述:使用task struct 結構
行程的組成方式:使用雙向鏈表把很多的task struct 變數給串起來(例如,當我們看到任務管理器可以認為,是作業系統內核遍歷了雙向鏈表,然后把每個節點的資訊獲取出來,并展示)
當我們創建一個行程時,實際上就是創建一個task struct 放到雙向鏈表中,當行程結束了就是從雙向鏈表中洗掉該節點,
2.2行程的組成
一個行程的task struct 里面大概都有啥樣的資訊
1)pid 行程ID 在任務管理器中也能看到

2)行程的記憶體指標:描述了行程持有的記憶體資源是哪些范圍(行程依賴的代碼和資料在哪里)
下面的資訊行程的組成為了輔助進行行程的“調度”
調度 如果在系統某一刻可能有100個行程,我們應該如何執行,
并行從微觀角度,每個行程和行程之間,是同時進行的,比如 8個CPU核心,可以同時進行8個行程
并發從微觀角度講,行程是串行執行的,從宏觀角度講,行程是“同時”進行的,
例如一個CPU負責執行3個行程,CPU先執行行程一,之后進行行程二,最后執行行程三,執行依次來回切換,由于CPU切換速度極快,從宏觀角度來看好像是三個行程同時執行,但是微觀角度依然是串行執行的,
3)行程的優先級多個行程存在,哪個行程要先執行
4)行程的背景關系記錄一次活動的相關狀態(某個行程在CPU上執行一段時間之后,就需要調度下去了,這時行程就需要把上次執行的狀態給記錄下來)記錄的內容:當時CPU的各個暫存器的值,記錄到記憶體中 記錄的目的,下次運行的時候,就能夠恢復上次的資訊,繼續向下執行
5)行程的記賬資訊:統計工具(每個行程在CPU上執行多久了,調度多少次了)
6)行程的狀態當前行程的狀態,影響調度
行程的調度
所謂的行程調度,其實就是一個搶占式執行的程序,這里的調度作業完全是由作業系統內核來實作,每個行程自身,不知道自己啥時候能獲取到CPU的執行,也不知道自己啥時候失去CPU,更不知道下一次獲取到CPU是啥時候,一切都是聽作業系統的,一切對于應用程式來說都是透明的,
2.3時間片
現代作業系統都支持多任務,就是作業系統可以同時運行多個任務
作業系統的任務調度是采用時間片輪轉的搶占式調度方式,也就是說任務執行一小段時間后強制暫停去執行下一個任務,每個任務輪流執行,
由于CPU執行的效率非常高,時間片非常短,在各個任務之間快速切換,給人一種多個任務同時執行的感覺,這就是我們所說的并發
2.4并行和并發
并發:多個行程在一個CPU下采用時間片輪轉的方式,在一段時間內,讓多個行程都得以推進,稱為并發
并行:多個行程在多個CPU下分別,同時進行運行,這稱為并行,
2.5內核態和用戶態
內核態,作業系統內核來執行任務
用戶態,應用程式來執行任務
完整的作業系統=作業系統內核+配套的應用程式
如果某個作業系統進入內核操作,就會變得不可控
2.6行程狀態
三種基本狀態
| 就緒 | 已經具備運行條件,但是由于沒有空閑CPU,而暫時不能運行 |
|---|---|
| 運行 | 占有CPU,正在CPU上運行 |
| 堵塞 | 等待某一事件的發生,暫時不能運行 |
另外兩種狀態
| 創建 | 行程正在被創建,作業系統為行程分配資源,初始化PCB |
|---|---|
| 結束 | 行程正在從系統中撤銷,作業系統會回收行程擁有的資源,撤銷PCB |
三、多執行緒
3.1執行緒是什么?
行程是為了實作并發編程的效果,但是為了追求效率就引入了執行緒(創建一個行程/銷毀一個行程,開銷比較大)因此就希望能夠更高效,更輕量的完成并發編程,通過執行緒來完成,執行緒也被稱為“輕量級行程”
每個執行緒就對應到一個“獨立的執行流”在這個執行流里就能夠完成一系列的指令,多個執行緒,就有了多個執行流,就可以并發的完成多個系列的指令了,
行程和執行緒的關系:
一個行程包含了多個執行緒
一個行程其實從系統這里申請了很多的系統資源~~,行程同一對這些資源進行管理,這個行程內的多個執行緒,共享了這些資源
行程具有獨立性:一個行程掉了,不會影響到其他行程
執行緒則不是這樣,如果一個執行緒壞了,其他的執行緒可能受到影響,
舉個例子來說明執行緒和行程
如果我們要吃100個西瓜

如果是引入多行程的方式就是

但是當我們創建新的行程時利用系統大量的開銷,也不太高效
這樣我們就引入多執行緒,把單執行緒變為多執行緒

在這種情況下,同樣可以達到并發編程,調度器也是可能會把兩個執行緒安排到不同的CPU上,開銷比多行程更低
但是執行緒越多越好嗎? 我們引入100個人吃西瓜,每個人只吃一個,豈不是更快?
所以增加執行緒可能會提高效率,但是一旦執行緒數量太多,就會擁擠不堪,這個時候,多個執行緒為了競爭CPU資源就會占更多的開銷(執行緒多了,調度的開銷也變大了)
3.2行程和執行緒的區別
1)行程時包含執行緒的~一個行程可以包含一個執行緒,也可以包含多個執行緒,
2)行程是資源分配的基本單位,執行緒是系統調度執行的基本單位
3)行程和行程之間是相互獨立的,行程A掛了,不會影響行程B,同一個行程的若干執行緒之間,共享這一資源(記憶體資源),如果某個執行緒出現例外,可能會導致整個行程終止,因此其他執行緒也就無法作業了,
3.3Java實作多執行緒
繼承Thread 重寫run 方法
public class Demo3 {
public static void main(String[] args) {
MyThread1 myThread1=new MyThread1();
myThread1.start();
while (true){
System.out.println("這是主執行緒!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread1 extends Thread{
@Override
public void run() {
//執行緒要執行的任意方法
while(true){
System.out.println("這是新執行緒!!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
結果:

多執行緒創建:
1)創建一個類繼承Thread
2)重寫Thread 的run方法 ,在新的run里面撰寫執行緒要執行的執行流,
3)創建子類實體
4)呼叫子類的start 方法.
3.4通過代碼演示多執行緒提高效率
1.先不使用多執行緒,直接串行執行
public class Demo4 {
//演繹多執行緒來提高程式的運行效率
//假設把兩個long型別的整數,給自增100億次
//1.先不使用多執行緒,直接串行執行
private static long count=100_0000_0000L;
public static void serial(){
long start=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("總執行時間"+(end-start)+"ms");
}
public static void main(String[] args) {
serial();
}
}
執行時間是10S左右:

2.使用多執行緒
public class Demo4 {
//演繹多執行緒來提高程式的運行效率
//假設把兩個long型別的整數,給自增100億次
//1.先不使用多執行緒,直接串行執行
private static long count=100_0000_0000L;
public static void serial(){
long start=System.currentTimeMillis();//毫秒級時間戳
long a=0;
for(long i=0;i<count;i++){
a++;
}
long b=0;
for(long i=0;i<count;i++){
b++;
}
long end=System.currentTimeMillis();
System.out.println("總執行時間"+(end-start)+"ms");
}
public static void concurrency(){
//2.并發執行
long beg=System.currentTimeMillis();
//創建兩個執行緒
Thread t1=new Thread(){
@Override
public void run() {
long a=0;
for(long i=0;i<count;i++){
a++;
}
}
};
t1.start();
Thread t2=new Thread(){
@Override
public void run() {
long b=0;
for(long i=0;i<count;i++){
b++;
}
}
};
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("執行時間"+(end-beg)+"ms");
}
public static void main(String[] args) {
//serial();
concurrency();
}
}

時間縮短了4S,為什么創建兩個執行緒時間不是縮短5S,因為創建出來的兩個執行緒不能保證完全是并行執行的~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/292494.html
標籤:其他
上一篇:面試招聘——計算機網路專場(一)
