你真的瞭解Android執行緒優先順序嗎?微信卡頓慘案分析

作者:leafjia,騰訊WXG客戶端開發工程師

你真的瞭解Android的執行緒優先順序嗎? 看似平平無奇的三行程式碼卻隱藏著巨大的陷阱!

Android上如果在主執行緒執行下面的程式碼:

Thread t = new Thread();t。start();t。setPriority(3);

我們的預期應該是子執行緒t的優先順序被設定為了低優先順序。

但真正執行後,我們驚奇的發現,不只是子執行緒t,主執行緒的優先順序同樣會被設定為低優先順序!事實上,這三行程式碼甚至導致了Android微信客戶端的一次線上故障!這是為什麼?背後有怎樣秘密?又如何管控和避免?我們來一起深入分析、研究下這個問題。

(傳送門:如果不想深入瞭解這其中的原理,和一波三折的故事,可以直接跳到最後的 5。3 小節,那裡提供了一些設定執行緒優先順序的正確和錯誤的典型例子)

一、案件發生

微信Android客戶端某個新版本釋出之後,馬上就收到了很多使用者的反饋:

公眾號裡的影片卡頓/音畫不同步

朋友圈裡的

影片卡頓掉幀

。看現象,初步可能會懷疑到是不是播放器有什麼問題?甚至懷疑是不是CDN有什麼問題?然而事情遠沒有想象的那麼簡單,在測試同學的幫助下,可以確定是Matrix的版本升級引起的。離譜!Matrix作為效能監控的工具,居然會影響到影片的播放?

二、案發現場

瀏覽器的同事先有了發現:音影片不同步,是因為pthread_cond_timedwait方法比設定的時間要長40ms左右導致。再經過測試,好傢伙!不止是這個方法,新版本微信裡,所有的

sleep/wait

方法,都要比原本設定的時間多出了

40ms

左右。可以用下面的程式碼,非常容易的復現:

long startTime = System。currentTimeMillis();Thread。sleep(10);Log。i(“Matrix”,“duration = ” + (System。currentTimeMillis() - startTime));//結果為:duration = 50

一開始我們完全沒有頭緒,根本不清楚到底是哪裡的程式碼引起的,好吧,最蠢的辦法,在新舊兩個Matrix版本的commit中,不斷二分,找到第一次出現問題的commit。終於找到了案發現場,定位到了類似這樣的一個修改:

你真的瞭解Android執行緒優先順序嗎?微信卡頓慘案分析

啊?只是

設定執行緒優先順序與啟動執行緒的順序調換

,況且設定的也只是一個特定子執行緒的優先順序,居然會有這麼大的破壞力?沒錯,讀者有興趣可以在Android上執行這樣一段程式碼:

Thread t = new Thread();t。start();t。setPriority(3);long startTime = System。currentTimeMillis();Thread。sleep(10);Log。i(“Matrix”,“duration = ” + (System。currentTimeMillis() - startTime)); // 結果為duration = 50ms

非常離奇的現象就是:如果在Thread的start後馬上呼叫setPriority,並且設定一個

小於預設優先順序

(預設優先順序為5)的優先順序,就會出現sleep/wait等方法會比設定的時間多幾十毫秒的現象。

而由於很多影片播放的邏輯中,都會透過系統的sleep/wait/pthread_cond_timedwait等方法來實現音影片的同步,會有

非常頻繁

的呼叫,如果每次都多出幾十毫秒,就會直接引起影片播放卡頓,音畫不同步等問題,也就直接導致了這次微信的線上故障。

三、分析推理

離譜!只是想設定一個特定子執行緒的優先順序,居然就直接影響了

主執行緒

主執行緒建立的所有子執行緒

的sleep時間?這裡絕對隱藏著什麼秘密。

(在一起來探秘之前,需要補充一個小的背景(如果你已經充分了解Linux執行緒的nice值,可以直接跳過):不管是在Java層設定執行緒優先順序,還是在Native層設定執行緒優先順序,最終設定的,也是絕大部分情況下最終起到作用的,都是執行緒的nice值,nice值越高,也就說明這個執行緒的“脾氣”越好,就越不容易搶到CPU,也就意味著執行緒的優先順序越低。)

3。1。 罪魁禍首:TimerSlackHigh

既然Thread的start和setPriority的順序改變會影響sleep/wait等方法的時間,我們就先看下setPriority做了什麼事情,Thread。setPriority最終會呼叫到

s

ystem/libartpalette/palette_android.cc

裡的

PaletteSchedSetPriority

方法:

palette_status_t PaletteSchedSetPriority(int32_t tid, int32_t managed_priority) { if (managed_priority < art::palette::kMinManagedThreadPriority || managed_priority > art::palette::kMaxManagedThreadPriority) { return PALETTE_STATUS_INVALID_ARGUMENT; } int new_nice = kNiceValues[managed_priority - art::palette::kMinManagedThreadPriority]; int curr_nice = getpriority(PRIO_PROCESS, tid); if (curr_nice == new_nice) { return PALETTE_STATUS_OK; } if (new_nice >= ANDROID_PRIORITY_BACKGROUND) { SetTaskProfiles(tid, {“SCHED_SP_BACKGROUND”}, true); } else if (curr_nice >= ANDROID_PRIORITY_BACKGROUND) { SchedPolicy policy; // Change to the sched policy group of the process。 if (get_sched_policy(getpid(), &policy) != 0) { policy = SP_FOREGROUND; } SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true); } if (setpriority(PRIO_PROCESS, tid, new_nice) != 0) { return PALETTE_STATUS_CHECK_ERRNO; } return PALETTE_STATUS_OK;}static const int kNiceValues[art::palette::kNumManagedThreadPriorities] = {    ANDROID_PRIORITY_LOWEST, // 1 (MIN_PRIORITY)    ANDROID_PRIORITY_BACKGROUND + 6,    ANDROID_PRIORITY_BACKGROUND + 3,    ANDROID_PRIORITY_BACKGROUND,    ANDROID_PRIORITY_NORMAL, // 5 (NORM_PRIORITY)    ANDROID_PRIORITY_NORMAL - 2,    ANDROID_PRIORITY_NORMAL - 4,    ANDROID_PRIORITY_URGENT_DISPLAY + 3,    ANDROID_PRIORITY_URGENT_DISPLAY + 2,    ANDROID_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY)};

該方法會根據

kNiceValues

陣列,把Java層Thread的優先順序對映為Linux的執行緒nice值,上面的例子中Java層priority是3,

kMinManagedThreadPriority

的值是1,所以經過對映後,得到的nice值為13,而

ANDROID_PRIORITY_BACKGROUND

是10,表示後臺優先順序,原來我們設定了比後臺優先順序的nice值更高的值(即比後臺優先順序更低),此時系統會把該執行緒設定為後臺執行緒,具體做了什麼呢?接著看

SetTaskProfiles

bool SetTaskProfiles(int tid, const std::vector& profiles, bool use_fd_cache) { return TaskProfiles::GetInstance()。SetTaskProfiles(tid, profiles, use_fd_cache);}TaskProfiles::TaskProfiles() { // load system task profiles if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) { LOG(ERROR) << “Loading ” << TASK_PROFILE_DB_FILE << “ for [” << getpid() << “] failed”; } //省略…… } static constexpr const char* TASK_PROFILE_DB_FILE = “/etc/task_profiles。json”;

這裡會去讀取

/etc/task_profiles.json

這個配置檔案,我們在這個配置檔案裡搜尋這裡傳入的引數

SCHED_SP_BACKGROUND

,找到了這樣的配置:

{ “Name”: “SCHED_SP_BACKGROUND”, “Profiles”: [ “HighEnergySaving”, “LowIoPriority”, “TimerSlackHigh” ]}

{ “Name”: “TimerSlackHigh”, “Actions”: [ { “Name”: “SetTimerSlack”, “Params”: { “Slack”: “40000000” } } ] }, { “Name”: “TimerSlackNormal”, “Actions”: [ { “Name”: “SetTimerSlack”, “Params”: { “Slack”: “50000” } } ]    }

被設定為後臺執行緒後,會設定三個profile:“HighEnergySaving”, “LowIoPriority”, “

TimerSlackHigh

”。

聰明的你應該敏銳的發現了這個

TimerSlackHigh

,“懶惰定時器”,名字看起來就非常可疑,很有可能跟我們在查的sleep/wait延遲有關,趕快查一下

TimerSlack

是什麼:

你真的瞭解Android執行緒優先順序嗎?微信卡頓慘案分析

原來真兇就是他!

TimerSlack是Linux系統為了降低系統功耗,避免timer時間參差不齊,過於的頻繁的喚醒cpu,而設定的一種對齊策略。

總而言之:如果系統設定了大於等於10的nice值,即設定了比後臺優先順序還要低的優先順序,即把執行緒設定成了後臺執行緒,那麼系統就會設定一個比較高的TimerSlack,從預設的50微秒,提高到40毫秒,從而導致wait/sleep等掛起的時間多了40ms左右。

但是

,還是不對勁啊,我設定的明明只是

特定子執行緒的優先順序

,按道理說只會影響該子執行緒的TimerSlack才對啊,為什麼看起來

影響了所有的執行緒

呢?確實,這個問題還有很多疑點,我們接著分析。

3。2。 大誤會:糊塗的setPriority

我們首先就會懷疑Java層呼叫setPriority,設定錯了執行緒,那麼找到

art/runtime/thread.cc

void Thread::SetNativePriority(int new_priority) {    palette_status_t status = PaletteSchedSetPriority(GetTid(), new_priority);    CHECK(status == PALETTE_STATUS_OK || status == PALETTE_STATUS_CHECK_ERRNO);}

在這裡之後才會呼叫之前提到的

PaletteSchedSetPriority

方法,我們發現這裡傳遞的引數也就是設定優先順序的物件執行緒是

GetTid()

,繼續找到

GetTid()

pid_t GetTid() const {    return tls32_。tid;}

tls32_是用來描述native執行緒的資料結構,繼續找下tls32_。tid是在哪裡賦值的,

art/runtime/thread.cc

void Thread::InitTid() { tls32_。tid = ::art::GetTid();}bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) { //…… InitTid(); //……}void* Thread::CreateCallback(void* arg) { //…… CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_。tmp_jni_env)); //……}void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { //…… pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread); //……}

一層一層往上找,來到

CreateNativeThread

方法,原來是線上程建立的時候在pthread_create的回撥方法

CreateCallback

裡面呼叫的InitTid,來設定的。而

CreateNativeThread

是Thread。start呼叫來建立native執行緒的地方。

對!你可能已經想到了,這裡是有

時序問題

的,pthread_create的回撥CreateCallback是非同步執行的,所以start執行完畢,並不能確保setNativePriority的tid引數已經賦值,而一旦tid還未被賦值,那麼這個時候呼叫setPriority,

tid就是預設值0

我們再看如果Native的系統呼叫setpriority的引數如果是0的話,會出現什麼情況:

你真的瞭解Android執行緒優先順序嗎?微信卡頓慘案分析

來了,如果第二個引數tid是0的話,就會設定

呼叫執行緒

的優先順序!

那TimerSlack呢?在tid為0的情況下的表現是什麼樣呢?我們找到最終設定TimerSlack的地方,

system/core/libprocessgroup/task_profiles.cpp

bool SetTimerSlackAction::ExecuteForTask(int tid) const { //…… if (tid == 0 || tid == GetThreadId()) { if (prctl(PR_SET_TIMERSLACK, slack_) == -1) { PLOG(ERROR) << “set_timerslack_ns prctl failed”; } } return true;}

TimerSlack最終是透過系統呼叫

prctl

方法設定的,如果tid是0,同樣會設定當前執行緒的TimerSlack。

所以結論是,如果GetTid為0,那麼無論是nice值還是TimerSlack都會對當前的呼叫執行緒設定。

居然有這麼詭異的陷阱!我們再回頭看引起問題的程式碼,似乎就說得過去了:

Thread t = new Thread(); t。start(); t。setPriority(3); long startTime = System。currentTimeMillis(); Thread。sleep(10); Log。i(“Matrix”,“duration = ” + (System。currentTimeMillis() - startTime));

t.start()呼叫之後,在native的tid還沒有被設定好的時候,就執行了下面的t.setPriority,這時GetTid返回值是0,native的setpriority的tid引數為0,就會把呼叫執行緒(Calling Thread)即主執行緒的nice設定為13,同時TimerSlack也被設定為40ms。有這樣的結果就說得通了。

然而,結束了嗎?因為時序問題,

主執行緒被設定了高nice和高TimerSlack

,理所當然的,主執行緒建立的子執行緒的nice值和TimerSlack理所當然地繼承了父執行緒,也就是主執行緒,所以自然也被影響了,是這樣嗎?

用這樣一段程式碼測試:

Thread t = new Thread();t。start();t。setPriority(3);long startTime = System。currentTimeMillis();Thread。sleep(10);Log。i(“Matrix”,“MainThread duration = ” + (System。currentTimeMillis() - startTime)); //結果為50new Thread(new Runnable() { @Override public void run() { long startTime = System。currentTimeMillis(); Thread。sleep(10); Log。i(“Matrix”,“Thread duration = ” + (System。currentTimeMillis() - startTime)); //結果為10 }})。start();

我們發現,主執行緒被錯誤設定nice值和TimerSlack後,建立的子執行緒並沒有繼承主執行緒的nice值和TimerSlack,這又是為什麼呢?我們繼續分析。

在native層的執行緒,的確

子執行緒會繼承父執行緒也就是主執行緒的nice值和TimerSlack

,但是,start和priority的時序問題,

只會

錯誤地設定主執行緒的native的nice值和TimerSlack,並不會影響主執行緒Java層的priority變數,而子執行緒同樣會在Java層繼承父執行緒Java層的priority,而在native層建立執行緒的時候,有這樣的邏輯:

void* Thread::CreateCallback(void* arg) { //…… ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority); self->SetNativePriority(priorityField->GetInt(self->tlsPtr_。opeer)); //……}

在native建立執行緒呼叫到CreateCallback時,會根據Java層的priority,重新設定native層的priority/nice值和TimerSlack,而在上面的情況中,主執行緒的Java層priority並沒有被設定,是預設的5,對應的nice值為0,所以子執行緒的nice值就被再次設定為了0,也就重新被設定為了前臺執行緒,TimerSlack就又被設定為了低TimerSlack。

那麼,問題又來了,既然主執行緒因為時序問題被錯誤地設定後臺優先順序後,並不影響其建立的子執行緒的nice值和TimerSlack,而線上故障中,引起音畫不同步和影片掉幀的執行緒,卻又都是在主執行緒建立的子執行緒中產生的。那又是什麼讓本來只會影響主執行緒的問題,進一步汙染了它建立的全部子執行緒呢?

3。3。 幫兇:WebView推波助瀾

再經過進一步排查,發現一個

非常奇怪的現象

,來看下面的程式碼:

Thread t = new Thread();t。start();t。setPriority(3);long startTime = System。currentTimeMillis();Thread。sleep(10);Log。i(“Matrix”,“MainThread duration = ” + (System。currentTimeMillis() - startTime)); //結果為50new WebView(context);new Thread(new Runnable() { @Override public void run() { long startTime = System。currentTimeMillis(); Thread。sleep(10); Log。i(“Matrix”,“Thread duration = ” + (System。currentTimeMillis() - startTime)); //結果為50 }})。start();

考驗眼力的時候來了,發現了跟之前的測試程式碼有什麼區別了嗎?是的,只要在建立子執行緒前加一句

new WebView(context)

,那麼子執行緒的nice和TimerSlack就也被影響了!

事實上,微信在某些頁面的確會有預載入webview的邏輯,我們也會發現,也的確在new了WebView之後,設定優先順序的時序錯誤,就不止會影響主執行緒,而是其建立的所有的子線執行緒就都會被汙染,都有了高TimerSlack,這是為什麼呢?

我們在new Webview前後列印一下主執行緒nice值就會發現,主執行緒nice值在執行new WebView之前是13,之後變成了-4。

哦?new WebView居然會設定主執行緒的優先順序?

找到Chromium的原始碼

content/browser/browser_main_loop.cc

// Up the priority of the UI thread unless it was already high (since Mac// and recent versions of Android (O+) do this automatically)。if (base::FeatureList::IsEnabled( features::kBrowserUseDisplayThreadPriority) &&    base::PlatformThread::GetCurrentThreadPriority() < base::ThreadPriority::DISPLAY) { base::PlatformThread::SetCurrentThreadPriority( base::ThreadPriority::DISPLAY);}

其中ThreadPriority::DISPLAY的值就是-4,是的,Chromium有這樣的邏輯:如果當前主執行緒的nice值大於-4(即優先順序較低),就會提高主執行緒的優先順序,並且這裡設定優先順序是直接呼叫了native的setpriority,所以

並不會同時修改TimerSlack

這個操作其實也很容易理解,是Chromium的保護措施,它不希望渲染UI的主執行緒處在一個較低的優先順序,不過卻陰差陽錯地成為了幫兇。

我們仔細看下,native執行緒在建立的時候根據Java層的priority設定nice值的時候的邏輯:

if (new_nice >= ANDROID_PRIORITY_BACKGROUND) {    SetTaskProfiles(tid, {“SCHED_SP_BACKGROUND”}, true);} else if (curr_nice >= ANDROID_PRIORITY_BACKGROUND) {    SchedPolicy policy;    // Change to the sched policy group of the process。    if (get_sched_policy(getpid(), &policy) != 0) {        policy = SP_FOREGROUND;    }    SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true);}

如果新設定的nice值大於等於10,直接判斷為後臺執行緒,設定高的TimerSlack;但另一方面,

只有當前的nice值大於10,新的nice值小於10,才會把執行緒設定為前臺執行緒,設定低的TimerSlack

而執行new WebView之後,主執行緒nice值是-4,而子執行緒繼承了主執行緒的nice值也是-4,

-4 < 10

,系統認為你已經是前臺執行緒了,就不會走到else if的邏輯,也就不會重新把執行緒設定為前臺執行緒,也就不會設定低TimerSlack,而子執行緒從主執行緒繼承的TimerSlack就是高TimerSlack。從而,主執行緒建立的全部子執行緒就都被汙染了,也就引起了,音畫不同步和掉幀的故障。

至此才真正破案了,所有的疑問和“奇怪”現象也都可以解釋通了。

該問題一開始是由Thread的start和setPriority的時序問題,導致主執行緒被設定為後臺執行緒,同時被設定了高TimerSlack;而幫兇Chromium又在初始化WebView的時候默默地把主執行緒的nice值設定成了較低的nice值(較高的優先順序),但又沒有設定回低TimerSlack,從而主執行緒建立的子執行緒繼承了主執行緒的nice值和高TimerSlack後,卻認為自己已經是前臺執行緒,所以也沒有機會根據Java層的priority重新設定低TimerSlack,最後就導致了主執行緒連同其建立的所有子執行緒的TimerSlack全部都被設定為了高TimerSlack,從而產生了一系列的問題,導致了這次故障。

四、監控機制:

原理已經搞清楚後,我們需要建立一個監控機制,避免之後再出現這種情況。

首先我們需要確保住主執行緒優先順序不被設定的過低,hook系統呼叫setpriority,如果對主執行緒設定過低的優先順序(過高的nice值),則直接報錯:

int (*original_setpriority)(int __which, id_t __who, int __priority);int my_setpriority(int __which, id_t __who, int __priority) { if (__priority <= 0) { return original_setpriority(__which, __who, __priority); } if (__who == 0 && getpid() == gettid()) { // Throw Crash Here } else if (__who == getpid()) { // Throw Crash Here } return original_setpriority(__which, __who, __priority);}

另外,我們還hook了設定TimerSlack的prctl方法,確保主執行緒的TimerSlack值不被設定的過大:

int (*original_prctl)(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);int my_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { if(option == PR_SET_TIMERSLACK) { if (gettid()==getpid() && arg2 > 50000) { // Throw Crash Here } } return original_prctl(option, arg2, arg3, arg4, arg5);}

監控會在微信各種體驗版和debug的時候開啟,確保當再次出現主執行緒的優先順序或者TimerSlack被錯誤設定的情況時,能夠提前發現,避免這類問題被帶到線上。

五、額外的結論:

最後,我們再討論下,在設定優先順序的時候我們容易出現的一些錯誤。

5。1。 執行緒優先順序的“雙標”

Thread在Java層的優先順序與Native層或者說Linux系統層的執行緒優先順序,也就是nice值,是兩套不同的標準,數字大小的意義甚至也是相反的,容易產生混淆和誤用。

透過

Thread.setPriority

方法設定的優先順序是在Java層的優先順序,數字從0到10,數字越大表優先順序越高,預設是5,Android主執行緒預設是5。Java層的優先順序透過native層的kNiceValues陣列,對映為nice值,再起到作用。

透過

android.os.Process.setThreadPriority

方法設定的優先順序是Native層的執行緒優先順序/nice值,數字從-20到20,數字越大代表優先順序越低,預設是0,Android主執行緒預設的nice值是-10。另外,native層的系統呼叫setpriority當然也是直接設定的nice值。

5。2。 正確設定HandlerThread的優先順序

怎麼設定HandlerThread的優先順序呢?第一反應可能會這樣寫

HandlerThread ht = new HandlerThread(“leafjia-thread”);ht。setPriority(3);ht。start();

似乎也沒什麼問題哦?但是這樣設定,其實除了設定了Java層Thread物件的成員變數priority,並不會起到任何其他的效果。

我們看HandlerThread的原始碼:

public class HandlerThread extends Thread { int mPriority; //…… public HandlerThread(String name) { super(name); mPriority = Process。THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) { super(name); mPriority = priority; } //…… @Override public void run() { mTid = Process。myTid(); Looper。prepare(); synchronized (this) { mLooper = Looper。myLooper(); notifyAll(); } Process。setThreadPriority(mPriority); onLooperPrepared(); Looper。loop(); mTid = -1; } //……}

呼叫ht。serPriority(3)其實設定的是其父類Thread的priority成員變數,而HandlerThread本身有自己的mPriority成員變數,start之後,會在建立Native Thread的時候,在呼叫run回撥方法前,根據Java層Thread的priority(我們已經設定為了3)設定Native的nice值,這時的確優先順序能夠設定成功。但是HandlerThread自己重寫了run方法,在之後執行的run方法中,又再次透過

Process.setThreadPriority(mPriority)

設定了自己的優先順序為mPriority,而mPriority並不能透過Thread。setPriority方法設定。所以上面的程式碼並不生效。

正確的方法也顯而易見:

HandlerThread ht = new HandlerThread(“My-Thread”, 13);ht。start();

需要注意的是,因為執行緒優先順序最終是透過Process。setThreadPriority方法實現的,所以priority使用的是-20到20的nice值的優先順序體系。

5。3。 Some Cases

進一步,可以總結出下面幾種設定執行緒優先順序的case,如果我們的目的是

設定thread執行緒的優先順序為3(而不想改變呼叫執行緒的優先順序),即nice值為13,那麼:

//Case1: 正確,thread執行緒的nice值被設定為13Thread thread = new Thread();thread。setPriority(3);thread。start();//Case2: 正確,thread執行緒的nice值被設定為13Thread thread = new Thread(new Runnable() { @Override public void run() { Thread。currentThread()。setPriority(3); // 或: // Process。setThreadPriority(13); }});thread。start();//Case3: 錯誤,thread執行緒的nice值一定被設定為13//(可能直接被設定或從當前執行緒繼承而來,視時序而定),// 當前執行緒的nice值很有可能被設定為13Thread thread = new Thread();thread。start();thread。setPriority(3);//Case4: 錯誤,同上HandlerThread thread = new HandlerThread(“My-Thread”);thread。start();thread。setPriority(3);//Case5: 錯誤,thread執行緒的nice值被設定為預設的0HandlerThread thread = new HandlerThread(“My-Thread”);thread。setPriority(3);thread。start();//Case6: 正確,thread執行緒的nice值被設定為13HandlerThread thread = new HandlerThread(“My-Thread”, 13);thread。start();

至此,就是本文的全部內容。執行緒優先順序的相關問題,大家平時可能關注的並不多,或者會認為執行緒優先順序的影響並不會特別大。而看起來平平無奇的設定執行緒優先順序的三行程式碼,就真的引起了一次線上故障。這也提醒大家,

一旦涉及到多執行緒,就一定要對時序問題特別謹慎。

如果這篇文章對你有幫助,歡迎

分享,收藏,點贊!