Java 并发总结——进程与线程
一、進程與線程
(1)線程與進程
? ? 進程是程序在一個數據集合上運行的過程,它是系統進行資源分配和調度的一個獨立單位。進程實體由程序段, 數據段 PCB(進程控制塊)組成。
? ?線程可以看做輕量級進程,線程是進程的執行單元,是進程調度的基本單位。
(2)多線程中的上下文切換
在上下文切換過程中,CPU 會停止處理當前運行的程序,并保存當前程序運行的具體位置以便之后繼續運行。在程序中,上下文切換過程中的“頁碼”信息是保存在進程控制塊(PCB)中的。PCB 還經常被稱作“切換楨”(switchframe)。“頁碼”信息會一直保存到 CPU 的內存中,直到他們被再次使用。
上下文切換是存儲和恢復 CPU 狀態的過程,它使得線程執行能夠從中斷點恢復執行。上下文切換是多任務操作系統和多線程環境的基本特征。
(3)多線程同步和互斥實現方法
1、線程同步
指線程之間所具有的一種制約關系,一個線程的執行依賴另一個線程的消息,當它沒有得到另一個線程的消息時應等待,直到消息到達時才被喚醒。
2、線程互斥
指對于共享的進程系統資源,在各單個線程訪問時的排它性。當有若干個線程都要使用某一共享資源時,任何時刻最多只允許一個線程去使用,其它要使用該資源的線程必須等待,直到占用資源者釋放該資源。線程互斥可以看成是一種特殊的線程同步。
線程間的同步方法大體可分為兩類:用戶模式和內核模式。
內核模式:指利用系統內核對象的單一性來進行同步,使用時需要切換內核態與用戶態,
用戶模式:就是不需要切換到內核態,只在用戶態完成操作。
用戶模式下的方法有:原子操作(例如一個單一的全局變量),臨界區。
內核模式下的方法有:事件,信號量,互斥量。
(3)進程調度算法
1、實時系統
FIFO(First Input First Output,先進先出算法)
SJF(Shortest Job First,最短作業優先算法)
SRTF(Shortest Remaining Time First,最短剩余時間優先算法)。
2、交互式系統
RR(Round Robin,時間片輪轉算法)
HPF(Highest Priority First,最高優先級算法)
多級隊列
最短進程優先
保證調度,彩票調度,公平分享調度。
二、Java線程
(1)創建線程的方式
1、繼承 Thread 類創建線程類
2、通過 Runnable 接口創建線程類
3、通過 Callable 和 Future 創建線程
4、通過線程池創建
(2)?守護線程和本地線程
在Java中有兩類線程:用戶線程 (User Thread)、守護線程 (Daemon Thread、gc線程、Finalizer線程)
守護線程和用戶線程的區別在于:守護線程依賴于創建它的線程,而用戶線程則不依賴。
擴展:
Thread Dump 打印出來的線程信息,含有 daemon 字樣的線程即為守護
進程,可能會有:服務守護進程、編譯守護進程、windows 下的監聽 Ctrl+break
的守護進程、Finalizer 守護進程、引用處理守護進程、GC 守護進程。
(3)Java線程的生命周期
新建(New):創建后尚位啟動的線程的狀態
運行(Runnable):包含Running和Ready
無限期等待(Waiting):不會被分配CPU執行時間,需要顯式唄喚醒
限期等待(Timed Waiting):在一定時間后會由系統自動喚醒
阻塞(Blocked):等待排它鎖
結束(Terminated):已終止線程的狀態,線程已經結束執行
(4)線程調度的方法
1、wait()
使一個線程處于等待(阻塞)狀態,并且釋放所持有的對象的鎖;
2、sleep()
使一個正在運行的線程處于睡眠狀態,是一個靜態方法,調用此方法要處理InterruptedException異常;
3、notify()
喚醒一個處于等待狀態的線程,當然在調用此方法的時候,并不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且與優先級無關;
4、notityAll()
喚醒所有處于等待狀態的線程,該方法并不是將對象的鎖給所有線程,而是讓它們競爭,只有獲得鎖的線程才能進入就緒狀態;
(5)線程調度算法
有兩種調度模型:分時調度模型和搶占式調度模型。
分時調度模型是指讓所有的線程輪流獲得 cpu 的使用權,并且平均分配每個線程占 用的 CPU 的時間片。搶占式調度模型是指優先讓可運行池中優先級高的線程占用
CPU,如果可運行池中的線程優先級相同,那么就隨機選擇一個線程,使其占用
CPU。處于運行狀態的線程會一直運行,直至它不得不放棄 CPU。
java虛擬機采用時間片輪轉的方式。可以設置線程的優先級,會映射到下層的系統上面的優
先級上,如非特別需要,盡量不要用,防止線程饑餓。
三、死鎖
(1)死鎖產生的4個必要條件
1、互斥條件
2、請求和保持條件
3、不可搶占條件
4)循環等待條件
(2)死鎖類型
1、鎖順序死鎖(交錯執行)
2、動態鎖順序死鎖
3、協作對象之間發生死鎖(隱式獲取兩個鎖)
(3)避免死鎖的方法
1、固定加鎖的順序(針對鎖順序死鎖)
2、開放調用(針對對象之間協作造成的死鎖)
3、使用定時鎖-->tryLock()
4、如果等待獲取鎖時間超時,則拋出異常而不是一直等待!
(4)死鎖檢測
JDK提供了兩種方式檢測:
JconsoleJDK自帶的圖形化界面工具,使用JDK給我們的的工具JConsole
Jstack是JDK自帶的命令行工具,主要用于線程Dump分析。
1、Jstack命令
jstack是java虛擬機自帶的一種堆棧跟蹤工具。jstack用于打印出給定的java進程ID或core file或遠程調試服務的Java堆棧信息。 Jstack工具可以用于生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。 線程出現停頓的時候通過jstack來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后臺做什么事情,或者等待什么資源。
2、JConsole工具
Jconsole是JDK自帶的監控工具,在JDK/bin目錄下可以找到。它用于連接正在運行的本地或者遠程的JVM,對運行在Java應用程序的資源消耗和性能進行監控,并畫出大量的圖表,提供強大的可視化界面。而且本身占用的服務器內存很小,甚至可以說幾乎不消耗。
(5)死鎖、活鎖、饑餓
1、死鎖
是指兩個或兩個以上的進程(或線程)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。
2、活鎖
任務或者執行者沒有被阻塞,由于某些條件沒有滿足,導致一直重復嘗試,
失敗,嘗試,失敗。
活鎖和死鎖的區別在于,處于活鎖的實體是在不斷的改變狀態,所謂的“活”, 而
處于死鎖的實體表現為等待;活鎖有可能自行解開,死鎖則不能。
3、饑餓
一個或者多個線程因為種種原因無法獲得所需要的資源,導致一直無法執
行的狀態。
Java 中導致饑餓的原因:
1)高優先級線程吞噬所有的低優先級線程的 CPU 時間。
2)線程被永久堵塞在一個等待進入同步塊的狀態,因為其他線程總是能在它之前
持續地對該同步塊進行訪問。
3)線程在等待一個本身也處于永久等待完成的對象(比如調用這個對象的 wait 方 法),因為其他線程總是被持續地獲得喚醒。
四、擴展
(1)避免死鎖
預防死鎖的幾種策略,會嚴重地損害系統性能。因此在避免死鎖時,要施加較弱的限制,從而獲得 較滿意的系統性能。由于在避免死鎖的策略中,允許進程動態地申請資源。因而,系統在進行資源分配之前預先計算資源分配的安全性。若此次分配不會導致系統進入不安全的狀態,則將資源分配給進程;否則,進程等待。其中最具有代表性的避免死鎖算法是銀行家算法。
銀行家算法:首先需要定義狀態和安全狀態的概念。系統的狀態是當前給進程分配的資源情況。因此,狀態包含兩個向量Resource(系統中每種資源的總量)和Available(未分配給進程的每種資源的總量)及兩個矩陣Claim(表示進程對資源的需求)和Allocation(表示當前分配給進程的資源)。安全狀態是指至少有一個資源分配序列不會導致死鎖。當進程請求一組資源時,假設同意該請求,從而改變了系統的狀態,然后確定其結果是否還處于安全狀態。如果是,同意這個請求;如果不是,阻塞該進程知道同意該請求后系統狀態仍然是安全的。
(2)檢測死鎖
首先為每個進程和每個資源指定一個唯一的號碼;
然后建立資源分配表和進程等待表。
(3)解除死鎖
當發現有進程死鎖后,便應立即把它從死鎖狀態中解脫出來,常采用的方法有:
奪資源:從其它進程剝奪足夠數量的資源給死鎖進程,以解除死鎖狀態;
撤消進程:可以直接撤消死鎖進程或撤消代價最小的進程,直至有足夠的資源可用,死鎖狀態.消除為止;所謂代價是指優先級、運行代價、進程的重要性和價值等。
五、問題
(1)進程與線程的區別
進程是資源分配的最小單位
線程是CPU調度的最小單位
一個程序必須有一個進程,一個進程必須有一個線程
(2)Thread中start和run方法的區別
調用start()方法會創建一個新的子線程并啟動
run()方法只是Thread的一個普通方法的調用
(3)Thread和Runnable的關系
Thread是實現了Runnable接口的類,使得run支持多線程
因類的單一繼承原則,推薦多使用Runnable接口
(4)wait()和sleep()的區別
sleep是Thread類的方法,wait是Object類中定義的方法
sleep()方法可以在任何地方使用
wait()方法只能在sychronized方法或synchronized塊中使用
Thread.sleep只會讓出CPU,不會導致鎖行為的改變
Object.wait不僅讓出CPU,還會釋放已經占有的同步資源鎖
(5)鎖池與等待池
在java中,每個對象都有兩個池,鎖(monitor)池和等待池
wait() ,notifyAll(),notify() 三個方法都是Object類中的方法.
鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由于這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權,但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中。
等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖(因為wait()方法必須出現在synchronized中,這樣自然在執行wait()方法之前線程A就已經擁有了該對象的鎖),同時線程A就進入到了該對象的等待池中。如果另外的一個線程調用了相同對象的notifyAll()方法,那么處于該對象的等待池中的線程就會全部進入該對象的鎖池中,準備爭奪鎖的擁有權。如果另外的一個線程調用了相同對象的notify()方法,那么僅僅有一個處于該對象的等待池中的線程(隨機)會進入該對象的鎖池.
(6)notify和notifyAll的區別
notifyAll:會讓所有處于等待池的線程全部進入鎖池去競爭獲取鎖的機會
notify:會隨機讓一個處于等待池中的線程進入鎖池去競爭獲取鎖的機會
(7)interrupt函數
通知線程應該中斷了
- 如果線程處于阻塞狀態,那么線程將立即退出被阻塞狀態,拋出一個InterruptedException異常
- 如果線程處于正常活動狀態,那么會將該線程的中斷標志設置為true。被設置中斷標志的線程將繼續正常運行,不受影響
(8)線程狀態以及狀態之間的轉換
總結
以上是生活随笔為你收集整理的Java 并发总结——进程与线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 张丰毅演过哪些电视剧 这四部您都看过那部
- 下一篇: Java 并发总结——AQS