JAVA深入理解並發、線程與等待通知機制(中)

大西瓜學java 發佈 2024-04-30T14:07:58.350841+00:00

線程的狀態和生命周期1:初始線程,也就是new Thread,但是還沒有調用start方法,還在JVM堆中,沒有和OS真正的線程掛鈎。

線程的狀態和生命周期

1:初始線程,也就是new Thread,但是還沒有調用start方法,還在JVM堆中,沒有和os真正的線程掛鈎。

2:運行(RUNNABLE):調用了start,這時候os才會真正創建線程並和我們JVM堆中的線程掛鈎,os創建完線程後,需要等待cpu調度,cpu還沒輪到在os層面是就緒狀態(ready),cpu輪到了就是運行中(running)這兩種狀態,在java層面統稱為運行狀態。

3:阻塞(BLOCKED):調用synchronized關鍵字。

4:等待(WAITING):比如調用了wait,join,park等等需要被喚醒。

5:超時等待(TIMED_WAITING):該狀態不同於WAITING,它可以自己執行時間比如sleep(long),wait(long)…. …. 。

6:中止:線程執行完成。

線程的優先級

在Thread類中有個變量priotity,通過setPriority(int)方法來修改優先級(1-10),默認是5,優先級搞的線程分配時間片的數量高於優先級低的線程。兩個線程同時處於READY優先越高的越容易被執行。

針對頻繁阻塞的線程需要設置偏重點的優先級確保不會被獨占,在不同的作業系統上,線程規劃會存在差異,有些則會忽略,在我們平常使用的時候一般使用默認的。

線程的調度

線程的調度是指cpu分配時間片給線程的過程,主要調度方式:

協同式線程調度:線程的執行時間由線程本身來控制,線程自己工作完成後,通知系統切換到另外一個線程,好處就是實現起來簡單,沒有線程同步問題,壞處就是會導致一直阻塞。

搶占式線程調度:每個線程執行的時間以及切換是由系統決定,線程的執行時間不可控,所以不會因為一個線程而導致其他的線程一直阻塞。JAVA中使用的是搶占式線程調度,Thread.yield可以讓線程讓出cpu資源,但是對於再次執行是由cpu決定的,優先級越高的越優先被執行。

內核線程的實現

內核的線程和用戶層面的線程有 1:1,1:N,N:M

內核和用戶層面線程比例1:1

內核線程和用戶線程一對一,線程的調度是由內核來操作調度器來堆線程進行調度。每個線程就是一個獨立單元,即使這個線程阻塞了異常了,也不會影響到其他的線程。

缺點就是,用戶態沒創建一個線程內核就必須創建個線程與之對應,用戶態切換內核態再切換用戶態。消耗資源,在JVM中每創建個線程都會創建個固定大小的線程棧。

內核和用戶層面線程比例1:N

線程是建立在用戶線程上的,用戶線程的建立,同步,調度,銷毀都需要在用戶態完成。不需要內核的幫忙,因此快速低消耗,也能夠支持更大的線程數。

缺點就是複雜,比如調度問題,線程阻塞問題等等。

內核和用戶層面線程比例N:M

這個對比1:N就好點,線程還是在用戶層面建立,但是調度就可以需要內核提供的線程調度功能。

協程

首先說下JAVA線程存在的多線程問題,用戶線程和內核線程是1:1關係,它的上下文切換和CPU調度是很花費成本的,系統容納的線程數量也有限,在一個線程中如何線程的執行時間長其實是無所謂的,但是如果執行的時間斷就簡單的處理了一兩條命令而去調用內核的線程,cpu去調度去上下文切換然後執行了一兩條命令,相對來說浪費。

協程概念:

協同式調度,通過上邊可以知道上下文切換和調度是花費時間,但是如果這部分由用戶態去實現,由我們自己去實現,去實現調用棧的保護和恢復工作,則就可以通過某些手段來縮減這些開銷。它的缺點繼承了協程式調度的缺點。

關鍵字: