雜談Android線程優先級

背景:最近在梳理Android線程調度的相關內容。在梳理過程中,閱讀了部分源碼,以及相關的介紹文章,甚至重新翻起了《Linux內核設計與實現》,但是距離理解透徹,並且能夠用自己的語言清晰無誤地闡述出來,感覺還有點遠,還有很多細節需要進一步理論結合實際。爲了避免在忙亂的生活節奏中,梳理的目標又草草結束。希望自己能夠把目標細分一下,先把幾個理解清晰的問題給記錄下來,通過不斷清晰地回答相關的問題,最終能夠完成整個原理的清晰理解與闡述。這篇文章,就是針對Android線程優先級方面,一個一個問題的回答,可能有些凌亂。如果有理解不到位的地方,也希望大家指出來。

問題一:Linux是用什麼來描述進程的優先級的?

Linux中存在實時進程,和普通進程。對於普通進程來講,使用nice來描述進程的優先級,取值範圍是[-20,19]。對於實時進程來講,則有一個實時優先級,取值範圍是[0,99]。如下圖所示,普通進程與實時進程的優先級對齊後,得到了進程的優先級prio。

整體來看,Linux中進程的優先級分爲三類:

  • 靜態優先級: 不會時間而改變,內核也不會修改,只能通過系統調用改變nice值的方法區修改。優先級映射公式: static_prio = MAX_RT_PRIO + nice + 20,其中MAX_RT_PRIO = 100,那麼取值區間爲[100, 139];對應普通進程;

  • 實時優先級:只對實時進程有意義,取值區間爲[0, MAX_RT_PRIO -1],其中MAX_RT_PRIO = 100,那麼取值區間爲[0, 99];對應實時進程;

  • 動態優先級: 調度程序通過增加或減少進程靜態優先級的值,來達到獎勵IO消耗型或懲罰cpu消耗型的進程,調整後的進程稱爲動態優先級。區間範圍[0, MX_PRIO-1],其中MX_PRIO = 140,那麼取值區間爲[0,139];

我們在Android手機上,可以通過adb shell ps -p -t -P來看到進程的優先級,如下所示:RTPRI字段表示real time priority,只對實時進程有有效。而NICE字段,只對普通進程有效。將RTPRI與NICE統一後,得到進程的PRIO,其中值越小,表示優先級越高。

image

問題二:java.lang.Thread.setPriority與android.os.Process.setThreadPriority有什麼區別和聯繫?

如何設置進程的優先級呢?一般有兩種方式,一種是通過java.lang.Thread.setPriority,還有一種是通過android.os.Process.setThreadPriority。這兩種方式的參數是有不同的,而且效果也有細微的差別。我們直接通過源碼來分析差別。

1、android.os.Process.setThreadPriority

分析android.os.Process.java的源碼,我們看到setThreadPriority是一個jni接口,最終實現在native層。其參數的定義在Proce3ss.java中定義了一些常量。結合前面的NICE值,我們知道這個接口應該是直接設置進程的nice值了。

具體是不是設置Linux中的nice值,我們可以看看native層的實現,Process.setThreadPriority對應native的實現在core/jni/android_util_Process.cpp中,代碼如下:

最終又調用到libutils中的Threads.cpp,最終到setpriority這個系統調用,可以明確看到就是設置進程的NICE值。

對應Process.java中的getThreadPriority最終也調用了系統調用getpriority。

2、java.lang.Thread.setPriority

通過Thread.java的源碼,我們看到對於priority的設置在MIN_PRIORITY(1),與MAX_PRIORITY(10)之間。按接口的描述,MAX_PRIORITY的優先級應該是大於MIN_PRIORITY,即優先級是遞增的。這個跟Linux的NICE值定義是不同的。其實是這個是java中對於線程優先級的規範,具體的實現是按虛擬機來。Android是運行在Linux的內核之上的,最終也要通過系統調用來設置進程的NICE值來調整進程的優先級的。這裏,我們有疑問的有兩個點,第一個點是,java的線程優先級如何跟NICE對應,第二個點是這個接口,跟前面Process.setThreadPriority除了優先級的定義不同,還有什麼差別嗎?

爲了解決這兩個問題,我們再分析native層的實現nativeSetPriority,代碼如下:我們看到java的priority與nice值之間有一個對應關係,其中MAX_PRIORITY對應了ANDROID_PRIORITY_URGENT_DISPLAY,也就是Process.java中的THREAD_PRIORITY_URGENT_DISPLAY,即-8。而MIN_PRIORITY對應了ANDROID_PRIORITY_LOWEST,也就是Process.java中的THREAD_PRIORITY_LOWEST。

另外,我們細心分析還發現一個問題,Thread的getPriority接口,直接返回了priority變量。而priority變量只有在Thread初始化的時候,補設置爲parent的優先級,以及在調用setPriority時,更新優先級。除此之外,沒有其他的數據同步操作。這意味着,如果不是通過java的Thread.setPriority更新的優先級,通過Thread.getPriority是無法同步更新的。

這個問題小結的結論是:

  • 無論是Thread.setPriority還是Process.setThreadPriority最終都會更新進程的nice值。

  • Thread.setPriority中的[MAX_PRIORITY,MIN_PRIORITY]對應了NICE值的[-8,19],可見Process.java的setThreadPriority對線程的優先級劃分得更加細。這就是爲什麼有人建議通過Process.setThreadPriority來設置線程的優先級的原因了,可以將優先級劃分的是更加細一些。

  • 在調整線程的優先級的過程中,也會調整線程的cgroups。

  • 在沒有明確設置的情況下,一個線程初始的優先級等於其parent的優先級。如果我們從UI線程來創建一個子線程的,那麼這個子線程的優先級就等於UI線程的優先級。

問題三:Android的一些異步線程組件是如何來設置線程的優先級的呢?

1、Thread

如果沒有給線程設置優先級,線程默認的優先級是調用new Thread的當前線程的優先級。由此可知,在UI線程創建一個子線程時,這個被創建的子線程的優先級直接等於UI線程的優先級。

2、AsyncTask 異步任務

分析AsyncTask的源碼我們看到,最終是通過調用Process.setThreadPriority來設置線程的優先級的,這裏默認的優先級是Process.THREAD_PRIORITY_BACKGROUND。再結合前面的代碼可以看到,如果設置的THREAD_PRIORITY_BACKGROUND(nice=10),最終會調整線程的調度策略,分配到SP_BACKGROUND調度組中,這樣可以避免影響UI主線程的響應。

3、HandlerThread

分析HandlerThread的代碼,可以看到,其默認的priority是Process.THREAD_PRIORITY_DEFAULT(nice=0),當然我們也可以設置指定的priority。最後也是通過Process.setThreadPriority來設置線程的優先級的。

4、ThreadPoolExecutor

ThreadPoolExecutor的線程最終是由ThreadFactory提供的,意味着線程的優先級由ThreadFactory來設置。一般來講,我們在實現ThreadFactory的newThread都會設置線程的優先級。

如果我們沒有設置ThreadFactory,則會使用默認的DefaultThreadFactory,默認的ThreadFactory設置了,線程的優先級爲Thread.NORM_PRIORITY,也就是對應ANDROID_PRIORITY_NORMAL(nice=0)

5、IntentService

IntentService內部使用了HandlerThread,而且沒有特別設置優先級。結合前面HandlerThread的分析,我們知道,其默認優先級是Process.THREAD_PRIORITY_DEFAULT(nice=0)。

 

後續:進程的優先級是如何影響進程調度?

見後續分析!

附加資料:

Android進階

移動架構師

 

需要這些安卓學習資料和麪試資料的大夥需要的關注+點贊+加羣:185873940 免費獲取!

點擊鏈接加入羣聊【Android IOC架構設計】:https://jq.qq.com/?_wv=1027&k=5tIZkaU

羣內還有許多免費的關於高階安卓學習資料,包括高級UI、性能優化、架構師課程、 NDK、混合式開發:ReactNative+Weex等多個Android技術知識的架構視頻資料,還有職業生涯規劃及面試指導。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章