並發編程 -- 多線程底層運行原理、線程狀態
作者 : Stanley 羅昊
多線程 -- 並發編程(一) : https://www.cnblogs.com/StanleyBlogs/p/10890906.html
【轉載請注明出處和署名,謝謝!】
多線程底層執行原理
說道底層運行,那么是不是就是需要依靠CPU啊;
那,各位之前有沒有聽過一句話叫做,一個CPU在同一個時間片只能執行一個程序;
什么意思呢?
就是,你的程序是不是都運行在一個CPU上啊,那你真正一個CPU在同一個時間片里是不是只能執行一個程序呀,那這個程序究竟要執行那個程序,是不是就需要通過線程之間時間片的一個爭搶;
時間片:微小的時間段;
多線程說白了就是時間片的爭奪,那個線程獲取了時間片,就執行那個線程的代碼;
假設,t1線程先獲得時間片,那么,t1線程就優先執行;
但是,它不可能拿着那個時間片不放吧,因為在CPU執行的過程中,底層運用輪循制的;
多線程執行的時候,CPU分配時間片是采取輪循的方式進行分配的;
就是輪流,有點像值日的時候,輪流值日一樣;
那,CPU在分配時間片的時候,第一個t1先搶占到了之后,他先執行了一段時間之后,CPU把這個t1執行完了以后,CPU是不是接着把時間片分配給t2去執行了;
那事實上,也是t2也在去搶占時間片;
當t1執行完畢后,那么,CPU就將迎來新的一輪爭奪,這個時候t2搶到了,就開始執行t2的代碼;
這就是多線程的底層執行原理;
多線程它在本質上運行的時候,他是同時執行的,還是輪流執行的呢?
肯定不是同時執行的,也就是不是我們常說的並發執行;那在你們看來,實際就是宏觀上你來看就是同時執行,但是在微觀上是不是的;
線程的狀態
線程總共有五種狀態;
第一個狀態 新建狀態
新建狀態,就是你新建一個線程是的狀態,也就是你新建了一個線程但還沒有啟動時的狀態;
當線程執行start方法的時候,就會進入就緒狀態;
第二個狀態 就緒狀態
進入就緒狀態的時候,事實上就是准備搶占CPU的時間片;
一旦搶占到了CPU的時間片它就會立即進入運行狀態;
第三個狀態 運行狀態
當線程搶占到了CPU時間片的時候,它才會運行,所以第三個狀態是,運行狀態;
在它的運行狀態中,還有可能執行一個代碼,Throad.sleep();睡眠;
就是在你執行的時候,突然讓你睡眠了,我都讓你這個線程睡眠了,你還有必要去爭奪這個CPU資源嗎?
就肯定沒有必要再去爭奪這個CPU資源了,那這個時候你就需要釋放CPU啊,對不對,你釋放之后,你下次再運行的時候,你就需要重新獲取CPU的時間片,所以這種狀態就叫做堵塞狀態;
第四個狀態 堵塞狀態與sleep方法
想讓線程阻塞,最常用的方式就是使用sleep,用sleep這個方法,可以使運行中的線程回到就緒狀態;
因為它需要重新搶占CPU資源的,所以,sleep狀態的最終目的是讓改線程回到就讀狀態;
就比如,我現在想讓這個線程,進我想讓它每次進入run方法中的for循環打印里寫一個睡眠,一遍循環遍歷輸出,一邊睡眠看會發生什么:
我在run方法中業務寫完后,我們測試一下該線程:
在上圖中,可以發現,我同時調用了兩次start方法,說明,我執行了兩次我一次性開啟了兩次線程,並且執行了兩次,我們看看會不會出現交替執行的情況:
從輸出結果來看,確實交替執行了並且,是倆倆執行的:
每過一秒,就會執行一次:
我就不繼續演示了;
所以,我們從中可以看出,不管哪個線程過來,t1也好t2也好,執行的時候,均睡眠一秒鍾,睡眠完一秒鍾之后,誰先醒了,誰就繼續向下執行,這個就是到點自然醒的;
也可以使用join來造成線程堵塞;
join
剛剛,我們在上面介紹了sleep,我們來看看join;
join():是線程加入
它底層執行的是,當你在執行一個線程的時候,如果遇到其他線程加入,則會先執行加入的線程,直到的加入的線程執行完成,才會繼續執行原來線程的任務;
什么意思呢?
就是說,還是上面那個t1,與t2的例子,那假設說,t1在執行的過程中,突然遇到了一個代碼t2.join,這時候,就會在這個時間片,優先執行t2的線程;
join()方法可以給一個參數,參數代表執行的毫秒;
第五個狀態 死亡狀態
線程執行完了,或因異常退出了,都會結束生命周期,這就是死亡狀態;