前言
之前花了一個星期回顧了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。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導航: