摘要:死亡狀態線程退出有可能是正常執行完成也有可能遇見異常退出。類有新建與死亡狀態返回其余狀態返回判斷線程是否存活。線程因某些原因進入阻塞狀態。執行同步代碼塊的過程中執行了當前線程放棄開始睡眠進入就緒狀態但是不會釋放鎖。
【java內存模型簡介
JVM中存在一個主存區(Main Memory或Java Heap Memory),Java中所有變量都是存在主存中的,對于所有線程進行共享,而每個線程又存在自己的工作內存(Working Memory),工作內存中保存的是主存中某些變量的拷貝,線程對所有變量的操作并非發生在主存區,而是發生在工作內存中,而線程之間是不能直接相互訪問,變量在程序中的傳遞,是依賴主存來完成的。而在多核處理器下,大部分數據存儲在高速緩存中,如果高速緩存不經過內存的時候,也是不可見的一種表現。在Java程序中,內存本身是比較昂貴的資源,其實不僅僅針對Java應用程序,對操作系統本身而言內存也屬于昂貴資源,Java程序在性能開銷過程中有幾個比較典型的可控制的來源。synchronized和volatile關鍵字提供的內存中模型的可見性保證程序使用一個特殊的、存儲關卡(memory barrier)的指令,來刷新緩存,使緩存無效,刷新硬件的寫緩存并且延遲執行的傳遞過程,無疑該機制會對Java程序的性能產生一定的影響。
【java線程的運行機制在java虛擬機進程中,執行程序代碼的任務是由線程看來完成的。每個線程都有一個獨立的程序計數器和方法調用棧。程序計數器:pc寄存器,當線程執行一個方法時,程序計數器指向方法區中下一條要執行的字節碼指令。方法調用棧:用來跟蹤線程運行中一系列方法的調用過程,棧中的元素稱為棧幀。每當線程調用一個方法,就會壓棧一個新幀,幀用來保存方法的參數,局部變量,運算過程中產生的臨時數據。java虛擬機的主線程是它從啟動類的main()方法開始運行。此外,用戶也可以創建自己的線程,兩種方式:繼承 Thread 類,實現 Runnable 接口。
但是運行一個線程必須使用Thread.strat(),切記:1.不可直接運行run(),直接運行run()只是單純的方法調用,并不會產出新的線程。2.不要隨意覆蓋start(),如果必須覆蓋記得首先調用super.start()。線程是不會順序執行的,一切都由操作系統調度決定,并且一個線程只能啟動一次,第二次啟動會拋出:IllegalThreadStateException,但是并不會影響之前啟動的線程工作。
public class MyRunnable implements Runnable{ @Override public void run() { System.out.println("runnable running"); } } public class MyThread extends Thread{ @Override public void run(){ System.out.println("thread running"); } }【java線程狀態
新建狀態:new 語句創建的狀態,此時它和其他java對象一樣,僅僅在堆中被分配了內存。
就緒狀態:當一個線程被其他線程調用了start(),此時jvm會為它創建程序計數器和方法調用棧。處于改狀態的線程位于可運行池,等待獲取CPU的執行權。
運行狀態:處于改狀態的線程占用CPU,正在執行程序代碼。如果計算機只有一個單核CPU那么永遠hi只有一個線程處于改狀態。只有處于就緒狀態的線程才可能成為運行狀態。
阻塞狀態:線程因為某些原因放棄了CPU暫停執行。此時線程放棄CPU的執行權,直到進入就緒狀態才可能再次變為運行狀態。阻塞狀態3中情況:
對象等待池阻塞:線程執行了某個對象的wait(),線程被jvm放入這個對象的等待池之中。(用sleep()方法的過程中,線程不會釋放對象鎖。而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法后本線程才進入對象鎖定池準備。)
對象同步鎖阻塞:線程試圖獲取對象的同步鎖,如果同步鎖已經被其他線程持有,jvm會把該線程放入對象鎖池中。
其他阻塞狀態:當前線程執行sleep(),或者調用其它線程的join()(把指定的線程加入到當前線程,可以將兩個交替執行的線程合并為順序執行的線程。比如在線程B中調用了線程A的Join()方法,直到線程A執行完畢后,才會繼續執行線程B。),或者發出了IO請求。
死亡狀態:線程退出run(),有可能是正常執行完成,也有可能遇見異常退出。但是都不會對其他線程造成影響。Thread類有isAlive()(新建與死亡狀態返回false,其余狀態返回true)判斷線程是否存活。
【線程調度一個單核CPU在一個時刻只能執行一個機器指令。線程只有通過獲得CPU才能執行自己的程序代碼。所謂多線程的并發執行,其實從宏觀上來看:各個線程輪流獲得CPU的使用權,分別執行各自的任務。jvm采用搶占式調度模型,是指先讓高優先級線程獲得CPU。如果優先級相同,隨機選擇一個執行。處于運行狀態的線程或一直執行,直到不得不放棄CPU,一般有如下原因:
1. jvm讓其放棄CPU轉入就緒狀態。 2. 線程因某些原因進入阻塞狀態。 3. 運行結束退出run()。
值得注意一點:java的線程優先級使用Thread.setPriority(int)設置,通常三個靜態常量選擇:Thread.MAX_PRIORITY(默認:10),Thread.MIN_PRIORITY(默認:1),Thread.NORM_PRIORITY(默認:5)。但是各個操作系統的線程優先級并不相同,所以為了確保程序能夠在不同平臺正常執行,我們只是用這三個值,不會使用1-10中的其他數字。常用方法:
Thread.sleep(long millis): 當前線程放棄CPU進入阻塞狀態,經過milli毫秒后恢復就緒狀態,不放棄對象鎖的持有。
Thread.yield(): 讓出CPU執行權進入就緒狀態,給另一個擁有相同或者大于優先級的線程,如果沒滿足條件的線程,則什么都不做。
Thread.join(): 當前線程調用另一個線程的join(),并且等待被調用線程執行完后再繼續執行。
Object.wait(): 當前線程必須擁有當前對象鎖。如果當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常。喚醒當前對象鎖的等待線程使用notify或notifyAll方法,也必須擁有相同的對象鎖,否則也會拋出IllegalMonitorStateException異常。waite()和notify()必須synchronized函數或synchronized block中進行調用。如果在non-synchronized函數或non-synchronized block中進行調用,雖然能編譯通過,但在運行時會發生IllegalMonitorStateException的異常。
Object.notify(): 執行該方法的線程隨機喚醒對象等待池中的一個線程,并將其裝入對象鎖池之中。
【線程的同步與并發并發編程三個概念:
原子性:一個操作或者多個操作,要么全部成功,要么全部失敗。
可見性:當多個線程訪問同一變量時,一個線程修改了該變量的值,其他線程能立即看到修改后的值。
有序性:程序執行的順序按照代碼的先后順序執行。(你以為這是廢話?請了解指令重排序)。這三個特性中2,3可以由volatile關鍵字保證(2.緩存一致性協議,3.禁止指令重排序),1只能由同步方式保證。
同步是解決資源共享的有效手段。當一個線程在操作共享變量的時候,其他線程只能等待。只有當該線程執行完同步代碼塊后,其他線程才能有機會操作共享資源。通常有如下幾種同步方式:
synchorized關鍵字: 修飾方法或者使用同步代碼塊。
ReentrantLock重入鎖對象: 鎖住共享變量的操作。
使用并發數據結構對象:Atomic系列,Concurrent系列等。
但是同步的操作,代價較大,我們應該盡可能減少同步操作,是的一個線程能盡快的釋放鎖,減少其他線程執行的時間。由于等待一個鎖的線程只有在獲得了這把鎖之后,
才能繼續執行所以讓持有鎖的線程及時釋放鎖的相當重要的。
以下情況線程釋放鎖:
執行完同步代碼塊。
執行同步代碼塊的過程中,遇見異常,線程死亡,鎖被釋放。
執行同步代碼塊的過程中,執行了鎖所屬對象的wait(),這個線程會釋放鎖進入對象等待池。
以下情況線程不會釋放鎖:
執行同步代碼塊的過程中,執行了Thread.sleep(),當前線程放棄CPU開始睡眠進入阻塞狀態,但是不會釋放鎖。
執行同步代碼塊的過程中,執行了Thread.yield(),當前線程放棄CPU開始睡眠進入就緒狀態,但是不會釋放鎖。
執行同步代碼塊的過程中,其他線程執行了當前線程的suspend()(已廢棄,同時廢棄的還有:Thread.stop(),Thread.resume()),當前線程被暫停,但是不會釋放鎖。 死鎖兩個線程互相等待對方持有的鎖,統統進入阻塞狀態,jvm不檢測也不避免這種情況。
【線程通信不同的線程需要協作完成工作(一種情況是:線程2需要線程1的執行結果)。
- Object.wait(): 執行該放大的線程釋放它持有的該對象的共享鎖(前提時必須持有該共享鎖),該線程進入對象等待池,等待其他線程將其喚醒。 - Object.notify(): 執行該方法的線程隨機喚醒對象等待池中的一個線程,并將其裝入對象鎖池之中。
補充:
1.鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由于這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權。但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中。
2.等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖(因為wait()方法必須出現在synchronized中,這樣自然在執行wait()方法之前線程A就已經擁有了該對象的鎖),同時線程A就進入到了該對象的等待池中。如果另外的一個線程調用了相同對象的notifyAll()方法,那么處于該對象的等待池中的線程就會全部進入該對象的鎖池中,準備爭奪鎖的擁有權。如果另外的一個線程調用了相同對象的notify()方法,那么僅僅有一個處于該對象的等待池中的線程(隨機)會進入該對象的鎖池。步驟如下(后面會有代碼實例):
1. t1執行s的一個同步代碼塊,t1持有s的共享鎖,t2在s的鎖池中等待。 2. t1在同步代碼中執行s.wait(0,t1釋放s的共享鎖,進入s的等待池。 3. s的鎖池中t2獲得共享鎖執行s的另一同步代碼塊。 4. t2在同步代碼塊中執行s.notify(),JVM將t1從s的等待池轉入s的鎖池。 5. t2完成同步代碼,釋放鎖,t1獲得鎖繼續執行同步代碼。
eg:兩個線程,一個線程將某個對象的某個成員變量的值加1,而另外一個線程將這個成員變量的值減1.使得該變量的值始終處于[0,2].初始值為0:
【中斷阻塞當一個線程處于阻塞狀態時,另一個線程調用阻塞線程的interrupt(),阻塞線程收到InterruptException,并退出阻塞狀態,開始進行異常處理。代碼:
@Override public void run() { System.out.println("runnable running"); try { Thread.sleep(1l); } catch (InterruptedException e) { //-----start異常處理---- e.printStackTrace(); //-----end異常處理----- } }【總結
并發編程的知識非常復雜,以上只是一些皮毛,后續還將學習Synchronized,ReentrantLock,Future,FutureTask,Executor,Fork/Join,CompletableFuture,Map-Reduce等相關知識,最后用一個實際項目來完成這部分知識的學習。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68063.html
摘要:相比與其他操作系統包括其他類系統有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。因為多線程競爭鎖時會引起上下文切換。減少線程的使用。很多編程語言中都有協程。所以如何避免死鎖的產生,在我們使用并發編程時至關重要。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)syn...
摘要:學習編程的本最佳書籍這些書涵蓋了各個領域,包括核心基礎知識,集合框架,多線程和并發,內部和性能調優,設計模式等。擅長解釋錯誤及錯誤的原因以及如何解決簡而言之,這是學習中并發和多線程的最佳書籍之一。 showImg(https://segmentfault.com/img/remote/1460000018913016); 來源 | 愿碼(ChainDesk.CN)內容編輯 愿碼Slo...
摘要:開始學習也有一段時間了,一些基礎的書也掃了一遍了。最近慢慢開始看和,后者的話和有類似之處,都是一些編程經驗的編程的世界里好多的東西都是相同的。這里其實是對的最佳實踐,之后該對象已經變成一個過期的引用了,此時就應該清空這個引用。 開始學習java也有一段時間了,一些基礎的書也掃了一遍了(think in java/core java volume 1)。最近慢慢開始看和,后者的話和有類似...
摘要:實戰高并發程序設計推薦豆瓣評分書的質量沒的說,推薦大家好好看一下。推薦,豆瓣評分,人評價本書介紹了在編程中條極具實用價值的經驗規則,這些經驗規則涵蓋了大多數開發人員每天所面臨的問題的解決方案。 很早就想把JavaGuide的書單更新一下了,昨晚加今天早上花了幾個時間對之前的書單進行了分類和補充完善。雖是終極版,但一定還有很多不錯的 Java 書籍我沒有添加進去,會繼續完善下去。希望這篇...
摘要:因為多線程競爭鎖時會引起上下文切換。減少線程的使用。舉個例子如果說服務器的帶寬只有,某個資源的下載速度是,系統啟動個線程下載該資源并不會導致下載速度編程,所以在并發編程時,需要考慮這些資源的限制。 最近私下做一項目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Jav...
閱讀 1386·2021-11-04 16:11
閱讀 3046·2021-10-12 10:11
閱讀 2980·2021-09-29 09:47
閱讀 1618·2021-09-22 15:40
閱讀 1016·2019-08-29 15:43
閱讀 2807·2019-08-29 13:50
閱讀 1582·2019-08-29 13:28
閱讀 2693·2019-08-29 12:54