前言
之前花了一個星期回顧了Java集合:
- Collection總覽
- List集合就這麼簡單【原始碼剖析】
- Map集合、雜湊表、紅黑樹介紹
- HashMap就是這麼簡單【原始碼剖析】
- LinkedHashMap就這麼簡單【原始碼剖析】
- TreeMap就這麼簡單【原始碼剖析】
- ConcurrentHashMap基於JDK1.8原始碼剖析
- Set集合就這麼簡單!
- Java集合總結【面試題+腦圖】,將知識點一網打盡!
在寫文章之前通讀了一遍《Java 核心技術 卷一》的併發章節和《Java併發程式設計實戰》前面的部分,回顧了一下以前寫過的筆記。從今天開始進入多執行緒的知識點咯~
之前在學習Java基礎的時候學多執行緒基礎還是挺認真的,可是在後面一直沒有回顧它,久而久之就把它給忘掉得差不多了..在學習JavaWeb上也一直沒用到多執行緒的地方(我做的東西太水了...)。
由於面試這一部分是佔很大比重的,並且學習多執行緒對我以後的提升也是很有幫助的(自以為)。
我其實也是相當於從零開始學多執行緒的,如果文章有錯的地方還請大家多多包含,不吝在評論區下指正呢~~
一、初識多執行緒
1.1介紹程式
講到執行緒,又不得不提程式了~
程式我們估計是很瞭解的了,在windows下開啟工作管理員,可以發現我們在作業系統上執行的程式都是程式:
程式的定義:
程式是程式的一次執行,程式是一個程式及其資料在處理機上順序執行時所發生的活動,程式是具有獨立功能的程式在一個資料集合上執行的過程,它是系統進行資源分配和排程的一個獨立單位
- 程式是系統進行資源分配和排程的獨立單位。每一個程式都有它自己的記憶體空間和系統資源
1.2回到執行緒
那系統有了程式這麼一個概念了,程式已經是可以進行資源分配和排程了,為什麼還要執行緒呢?
為使程式能併發執行,系統必須進行以下的一系列操作:
- (1)建立程式,系統在建立一個程式時,必須為它分配其所必需的、除處理機以外的所有資源,如記憶體空間、I/O裝置,以及建立相應的PCB;
- (2)撤消程式,系統在撤消程式時,又必須先對其所佔有的資源執行回收操作,然後再撤消PCB;
- (3)程式切換,對程式進行上下文切換時,需要保留當前程式的CPU環境,設定新選中程式的CPU環境,因而須花費不少的處理機時間。
可以看到程式實現多處理機環境下的程式排程,分派,切換時,都需要花費較大的時間和空間開銷
引入執行緒主要是**為了提高系統的執行效率,減少處理機的空轉時間和排程切換的時間,以及便於系統管理。**使OS具有更好的併發性
- 簡單來說:程式實現多處理非常耗費CPU的資源,而我們引入執行緒是作為排程和分派的基本單位(取代程式的部分基本功能**【排程】**)。
那麼執行緒在哪呢??舉個例子:
也就是說:在同一個程式內又可以執行多個任務,而這每一個任務我就可以看出是一個執行緒。
- 所以說:一個程式會有1個或多個執行緒的!
1.3程式與執行緒
於是我們可以總結出:
- 程式作為資源分配的基本單位
- 執行緒作為資源排程的基本單位,是程式的執行單元,執行路徑(單執行緒:一條執行路徑,多執行緒:多條執行路徑)。是程式使用CPU的最基本單位。
執行緒有3個基本狀態:
- 執行、就緒、阻塞
執行緒有5種基本操作:
- 派生、阻塞、啟用、 排程、 結束
執行緒的屬性:
- 1)輕型實體;
- 2)獨立排程和分派的基本單位;
- 3)可併發執行;
- 4)共享程式資源。
執行緒有兩個基本型別:
-
- 使用者級執行緒:管理過程全部由使用者程式完成,作業系統核心心只對程式進行管理。
-
- 系統級執行緒(核心級執行緒):由作業系統核心進行管理。作業系統核心給應用程式提供相應的系統呼叫和應用程式介面API,以使使用者程式可以建立、執行以及撤消執行緒。
值得注意的是:多執行緒的存在,不是提高程式的執行速度。其實是為了提高應用程式的使用率,程式的執行其實都是在搶CPU的資源,CPU的執行權。多個程式是在搶這個資源,而其中的某一個程式如果執行路徑比較多,就會有更高的機率搶到CPU的執行權
1.4並行與併發
並行:
- 並行性是指同一時刻內發生兩個或多個事件。
- 並行是在不同實體上的多個事件
併發:
- 併發性是指同一時間間隔內發生兩個或多個事件。
- 併發是在同一實體上的多個事件
由此可見:並行是針對程式的,併發是針對執行緒的。
1.5Java實現多執行緒
上面說了一大堆基礎,理解完的話。我們回到Java中,看看Java是如何實現多執行緒的~
Java實現多執行緒是使用Thread這個類的,我們來看看Thread類的頂部註釋:
通過上面的頂部註釋我們就可以發現,建立多執行緒有兩種方法:
- 繼承Thread,重寫run方法
- 實現Runnable介面,重寫run方法
1.5.1繼承Thread,重寫run方法
建立一個類,繼承Thread,重寫run方法
public class MyThread extends Thread {
@Override
public void run() {
for (int x = 0; x < 200; x++) {
System.out.println(x);
}
}
}
複製程式碼
我們呼叫一下測試看看:
public class MyThreadDemo {
public static void main(String[] args) {
// 建立兩個執行緒物件
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
my1.start();
my2.start();
}
}
複製程式碼
1.5.2實現Runnable介面,重寫run方法
實現Runnable介面,重寫run方法
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(x);
}
}
}
複製程式碼
我們呼叫一下測試看看:
public class MyRunnableDemo {
public static void main(String[] args) {
// 建立MyRunnable類的物件
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my);
Thread t2 = new Thread(my);
t1.start();
t2.start();
}
}
複製程式碼
結果還是跟上面是一樣的,這裡我就不貼圖了~~~
1.6Java實現多執行緒需要注意的細節
不要將run()
和start()
搞混了~
run()和start()方法區別:
run()
:僅僅是封裝被執行緒執行的程式碼,直接呼叫是普通方法start()
:首先啟動了執行緒,然後再由jvm去呼叫該執行緒的run()方法。
jvm虛擬機器的啟動是單執行緒的還是多執行緒的?
- 是多執行緒的。不僅僅是啟動main執行緒,還至少會啟動垃圾回收執行緒的,不然誰幫你回收不用的記憶體~
那麼,既然有兩種方式實現多執行緒,我們使用哪一種???
一般我們使用實現Runnable介面
- 可以避免java中的單繼承的限制
- 應該將併發執行任務和執行機制解耦,因此我們選擇實現Runnable介面這種方式!
二、總結
這篇主要是講解了執行緒是什麼,理解執行緒的基礎對我們往後的學習是有幫助的。這裡主要是簡單的入了個門
在閱讀頂部註釋的時候我們發現有”優先順序“、”後臺執行緒“這類的詞,這篇是沒有講解他們是什麼東西的~所以下一篇主要講解的是Thread的API~敬請期待哦~
使用執行緒其實會導致我們資料不安全,甚至程式無法執行的情況的,這些問題都會再後面講解到的~
之前在學習作業系統的時候根據《計算機作業系統-湯小丹》這本書也做了一點點筆記,都是比較淺顯的知識點。或許對大家有幫助~
參考資料:
- 《Java 核心技術卷一》
- 《Java併發程式設計實戰》
- 《計算機作業系統-湯小丹》
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。
文章的目錄導航: