【Java】撩开Java线程的“神秘面纱”
文章目錄
- 一.用一句話講明白進(jìn)程和線程的關(guān)系(不甚嚴(yán)密)
- 二.淺析進(jìn)程與線程
- 三.多線程
- 四.Java中的線程是什么樣子的
- 五.java.lang.Thread & java.lang.Runnable
- Thread
- Runnable
- 使用run()方法——線程運行體
- 六.進(jìn)程的生命周期(五狀態(tài)圖)
- 七.線程的生命周期
- Java線程的生命周期(五狀態(tài)圖)
- Java線程的生命周期(七狀態(tài)圖)
- 八.Java線程的主要API
- 九.Java線程的優(yōu)先級
- 線程的優(yōu)先級
- 表示線程優(yōu)先級的常量
- 線程優(yōu)先級方法
- 十.Java線程的啟動
- 十一.Java線程的休眠
- 十二.線程的終止
- 十三.Object類中線程的相關(guān)高級方法
- 十四.線程同步與synchronized
- synchronized與互斥鎖
- private與synchronized修飾的變量
- synchronized兩種同步方式的優(yōu)缺點
- 深入分析synchronized
- 使用synchronized實現(xiàn)線程安全的懶漢式單例模式
- 十五.Java線程死鎖問題
- 十六.并發(fā)和并行的區(qū)別
- 十七.致謝
一.用一句話講明白進(jìn)程和線程的關(guān)系(不甚嚴(yán)密)
操作系統(tǒng)可以同時執(zhí)行多個任務(wù),每個任務(wù)就是進(jìn)程;一個進(jìn)程可以同時執(zhí)行多個任務(wù),每個任務(wù)就是一個線程。
二.淺析進(jìn)程與線程
每個正在系統(tǒng)上運行的程序都是一個進(jìn)程。每個進(jìn)程包含一到多個線程。進(jìn)程也可能是整個程序或者是部分程序的動態(tài)執(zhí)行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨立執(zhí)行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進(jìn)程,它負(fù)責(zé)在單個程序里執(zhí)行多任務(wù)。通常由操作系統(tǒng)負(fù)責(zé)多個線程的調(diào)度和執(zhí)行。
線程是操作系統(tǒng)能夠進(jìn)行運算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實際運作單位。一條線程指的是進(jìn)程中一個單一順序的控制流,一個進(jìn)程中可以并發(fā)多個線程,每條線程并行執(zhí)行不同的任務(wù)。在Unix System V及SunOS中也被稱為輕量進(jìn)程,但輕量進(jìn)程更多指內(nèi)核線程,而把用戶線程稱為線程。
線程是獨立調(diào)度和分派的基本單位。線程可以為操作系統(tǒng)內(nèi)核調(diào)度的內(nèi)核線程,如Win32線程;由用戶進(jìn)程自行調(diào)度的用戶線程,如Linux平臺的POSIX Thread;或者由內(nèi)核與用戶進(jìn)程,如Windows 7的線程,進(jìn)行混合調(diào)度。
同一進(jìn)程中的多條線程將共享該進(jìn)程中的全部系統(tǒng)資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進(jìn)程中的多個線程有各自的調(diào)用棧,自己的寄存器環(huán)境,自己的線程本地存儲。
線程和進(jìn)程的區(qū)別在于,子進(jìn)程和父進(jìn)程有不同的代碼和數(shù)據(jù)空間,而多個線程則共享數(shù)據(jù)空間,每個線程有自己的執(zhí)行堆棧和程序計數(shù)器為其執(zhí)行上下文.多線程主要是為了節(jié)約CPU時間,發(fā)揮利用,根據(jù)具體情況而定. 線程的運行中需要使用計算機(jī)的內(nèi)存資源和CPU。
(這里基于Java講多線程,所以并沒有過于強(qiáng)調(diào)進(jìn)程。其實,進(jìn)程是操作系統(tǒng)的“主角”,對進(jìn)程了解不足建議閱讀《操作系統(tǒng)概念》(《Operating System Concepts》),從理論上全面理解進(jìn)程。)
三.多線程
多線程,是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計算機(jī)因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進(jìn)而提升整體處理性能。具有這種能力的系統(tǒng)包括對稱多處理機(jī)、多核心處理器以及芯片級多處理或同時多線程處理器。在一個程序中,這些獨立運行的程序片段叫作“線程”,利用它編程的概念就叫作“多線程處理”。
簡而言之,線程是程序中一個單一的順序控制流程。在單個程序中同時運行多個線程完成不同的工作,稱為多線程。
多線程編程的優(yōu)點:
- 進(jìn)程之間不易共享內(nèi)存,但線程之間共享內(nèi)存非常容易。
- 系統(tǒng)創(chuàng)建進(jìn)程時需要為該進(jìn)程重新分配系統(tǒng)資源,但創(chuàng)建線程的代價則小得多,因此使用多線程來實現(xiàn)多任務(wù)并發(fā)比多進(jìn)程效率高。
- Java語言內(nèi)置了多線程功能的支持,而不是單純地作為底層操作系統(tǒng)的調(diào)度方式,從而簡化了Java的多線程編程。
四.Java中的線程是什么樣子的
Java程序是通過線程執(zhí)行的,線程在程序中具有獨立的執(zhí)行路徑。當(dāng)多條線程執(zhí)行時,它們的路徑可以不同。
每一個Java應(yīng)用程序都需要有一個執(zhí)行main()函數(shù)的默認(rèn)主線程。應(yīng)用程序也可以創(chuàng)建線程在后臺操作時間密集型任務(wù),以確保對用戶的響應(yīng)。這些封裝了代碼執(zhí)行序列的線程對象就被成為runnable。
JVM給了每個線程分配了獨立的JVM棧空間以免互相干擾。獨立的棧使得線程可以追蹤他們自己下一條將要執(zhí)行的指令,這些指令會依線程的不同而有所區(qū)別。棧空間也為每條線程單獨準(zhǔn)備了一份方法參數(shù)、局部變量以及返回值的拷貝。
Java主要是基于java.lang.Thread類以及java.lang.Runnable接口來實現(xiàn)基本的線程機(jī)制的。
Thread類為底層操作系統(tǒng)的線程體系架構(gòu)提供一套統(tǒng)一的接口(通常OS負(fù)責(zé)創(chuàng)建和管理線程)。操作系統(tǒng)線程和Thread對象關(guān)聯(lián)(具體怎么關(guān)聯(lián)關(guān)系還要看OS的設(shè)計)。
Runnable接口則為關(guān)聯(lián)Thread對象的線程提供執(zhí)行代碼。這些代碼放在Runnable的void run()方法中,這個方法雖然不接收任何參數(shù)且沒有返回值,但有可能拋出異常。
五.java.lang.Thread & java.lang.Runnable
Thread
這個類里面有大量不常見的關(guān)鍵詞:
- volatile
- native
- synchronized
Runnable
package java.lang;@FunctionalInterface public interface Runnable {public abstract void run(); }我們?nèi)サ糇⑨?#xff0c;不算空行,其實Runnable接口只有5行:
使用run()方法——線程運行體
要將一段代碼(線程體)在一個新的線程上運行,該代碼應(yīng)該在一個線程類的run()函數(shù)中。
- 寫一個類implements Runnable接口,就必須重寫Runnable接口中的run()方法
- 寫一個類extends Thread類,就應(yīng)該重寫Thread類的run()方法
六.進(jìn)程的生命周期(五狀態(tài)圖)
下圖為進(jìn)程的五狀態(tài)圖:
七.線程的生命周期
Java線程的生命周期(五狀態(tài)圖)
Java線程的生命周期(七狀態(tài)圖)
八.Java線程的主要API
| isAlive() | 判斷線程是否還未被終止 |
| getPriority() | 獲取線程優(yōu)先級 |
| setPriority() | 設(shè)置線程優(yōu)先級 |
| Thread.sleep() | 線程休眠 |
| join() | 等待調(diào)用線程運行的結(jié)束,再執(zhí)行當(dāng)前線程 |
| yield() | 讓出資源,進(jìn)入就緒隊列等候 |
九.Java線程的優(yōu)先級
線程的優(yōu)先級
- Java提供一個線程調(diào)度器來監(jiān)控程序中啟動后進(jìn)入就緒狀態(tài)的所有線程,線程調(diào)度器按照線程的優(yōu)先級來決定應(yīng)調(diào)度哪個線程來執(zhí)行
- Java線程的優(yōu)先級用1~10的整數(shù)來表示,越小則優(yōu)先級越低
- Java的優(yōu)先級是高度依賴于操作系統(tǒng)的實現(xiàn)的
表示線程優(yōu)先級的常量
Thread類有三個常量,表示常用的線程優(yōu)先級:
- Thread.MIN_PRIORITY //1
- Thread.NORM_PRIORITY // 5
- Thread.MAX_PRIORITY // 10
線程默認(rèn)是NORM_PRIORITY,優(yōu)先級為5,包括main()線程。
線程優(yōu)先級方法
- getPriority():確定線程的優(yōu)先級
- setPriority():設(shè)置線程的優(yōu)先級
十.Java線程的啟動
run()方法內(nèi)是線程執(zhí)行的代碼段,但一定要記住:不是用run()啟動線程!!!
應(yīng)該用start()啟動!!!
十一.Java線程的休眠
與線程休眠有關(guān)的方法有三個:
- sleep()
- 讓線程中止一段時間的靜態(tài)方法
- Thread.sleep(long millis):暫時停止執(zhí)行millis毫秒
- 在睡眠期滿的瞬間,再次調(diào)用該線程不一定會恢復(fù)它的執(zhí)行,因為它很可能在等待隊列中
- join()
- 導(dǎo)致當(dāng)前線程等待,直到調(diào)用這個 join 方法的線程終止
- join( )
- join(long millis)
- join(long millis,int nanos)
- yield()
- 為其他可運行的線程提供執(zhí)行機(jī)會
- 靜態(tài)方法 — Thread.yield( )
十二.線程的終止
- 自動終止:一個線程完成執(zhí)行后,不能再次運行
- 手動終止:
- stop():已過時,基本不用
- interrupt(): 粗暴的終止方式
- 可通過使用一個標(biāo)志指示 run() 退出,從而終止線程
十三.Object類中線程的相關(guān)高級方法
- void wait():導(dǎo)致當(dāng)前的線程等待,直到其他線程調(diào)用此對象的 notify()方法或 notifyAll() 方法
- void notify():喚醒在此對象監(jiān)視器上等待的單個線程
- void notifyAll():喚醒在此對象監(jiān)視器上等待的所有線程
十四.線程同步與synchronized
線程同步最簡單的方法是使用synchronized關(guān)鍵詞。
該關(guān)鍵詞可以用于修飾變量或者方法。
- synchronized修飾變量:synchronized (obj){// 需要被同步的代碼 }
- synchronized修飾方法:public synchronized void function(){//同步方法的內(nèi)容 }
synchronized與互斥鎖
在Java語言中,引入了對象互斥鎖的概念,來保證共享數(shù)據(jù)操作的完整性。
- 每個對象都對應(yīng)于一個可稱為“互斥鎖”的標(biāo)記,這個標(biāo)記用來保證在任一時刻,只能有一個線程訪問該對象
- 關(guān)鍵字synchronized來與對象的互斥鎖聯(lián)系。當(dāng)某個對象用synchronized修飾時,表明該對象在任一時刻只能由一個線程訪問
- 同步的局限性:導(dǎo)致程序的執(zhí)行效率要降低
- 同步方法(非靜態(tài)的)的鎖為this
- 同步方法(靜態(tài)的)的鎖為當(dāng)前類本身
private與synchronized修飾的變量
受到synchronized保護(hù)的程序代碼塊和方法中,要訪問的對象屬性必須設(shè)定為private,因為如果不設(shè)定為private,那么就可以用不同的方式來訪問它,這樣就達(dá)不到保護(hù)的效果了。
synchronized兩種同步方式的優(yōu)缺點
-
synchronized方法
優(yōu)點:- 可以顯示的知道哪些方法是被synchronized關(guān)鍵字保護(hù)的
缺點:
- 方法中有些內(nèi)容是不需要同步的,如果該方法執(zhí)行會花很長時間,那么其他人就要花較多時間等待鎖被歸還
- 只能取得自己對象的鎖,有時候程序設(shè)計的需求,可能會需要取得其他對象的鎖
-
synchronized代碼塊
優(yōu)點:- 可以針對某段程序代碼同步,不需要浪費時間在別的程序代碼上
- 可以取得不同對象的鎖
缺點:
- 無法顯示的得知哪些方法是被synchronized關(guān)鍵字保護(hù)的
深入分析synchronized
synchronized關(guān)鍵詞實際上使用管程ADT實現(xiàn),原先效率很低,Java的幾個重要版本中經(jīng)歷不斷優(yōu)化已經(jīng)好很多了,但還是慢。
個人覺得大家可以這么去理解synchronized:所謂多線程程序,其實是巧妙地利用了多核系統(tǒng)的并發(fā)并行,并發(fā)與并行對其很重要。但是,synchronized與所謂管程(操作系統(tǒng)的概念),正是對這里開刀,它不給你控制的機(jī)會,直接保證臨界區(qū)(操作系統(tǒng)的概念)代碼段的執(zhí)行從并發(fā)、并行到串行,一次只能走一條線程。它相當(dāng)于一種悲觀鎖,直接粗暴地化并發(fā)、并行為串行,保證了并發(fā)程序的同步與共享數(shù)據(jù)一致性。
使用synchronized實現(xiàn)線程安全的懶漢式單例模式
public class Singleton {private static Singleton instance = null;private Singleton() {} public static Singleton getInstance() { if(instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }測試類:
public class SingletonTest { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1==s2); } }十五.Java線程死鎖問題
(不規(guī)范解釋) 死鎖:兩個線程,彼此在等待對方釋放其持有的鎖,而陷入無線等待。
鎖的歸還幾種方式:
- 基本上執(zhí)行完同步的程序代碼后,鎖就會自動歸還
- 用break語句跳出同步的語句塊,不過這對于寫在方法聲明的 synchronized沒有作用
- 遇到return語句
- 遇到異常
十六.并發(fā)和并行的區(qū)別
薦讀:《并發(fā)和并行的區(qū)別》
十七.致謝
- 感謝@碼農(nóng)云帆哥的指正,已改
總結(jié)
以上是生活随笔為你收集整理的【Java】撩开Java线程的“神秘面纱”的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过“FBI树”复习二叉树算法(洛谷P1
- 下一篇: 【计算机组成原理】Chapter1-复习