日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者: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最終會調用到system/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<std::string>& 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));  
//結果為50


new 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));  
//結果為50


new 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值被設置為13
Thread thread = new Thread();
thread.setPriority(3);
thread.start();


//Case2: 正確,thread線程的nice值被設置為13
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        Thread.currentThread().setPriority(3);
        // 或:
        // Process.setThreadPriority(13);
    }
});
thread.start();


//Case3: 錯誤,thread線程的nice值一定被設置為13
//(可能直接被設置或從當前線程繼承而來,視時序而定),
// 當前線程的nice值很有可能被設置為13
Thread thread = new Thread();
thread.start();
thread.setPriority(3);


//Case4: 錯誤,同上
HandlerThread thread = new HandlerThread("My-Thread");
thread.start();
thread.setPriority(3);


//Case5: 錯誤,thread線程的nice值被設置為默認的0
HandlerThread thread = new HandlerThread("My-Thread");
thread.setPriority(3);
thread.start();


//Case6: 正確,thread線程的nice值被設置為13
HandlerThread thread = new HandlerThread("My-Thread", 13);
thread.start();

至此,就是本文的全部內容。線程優先級的相關問題,大家平時可能關注的并不多,或者會認為線程優先級的影響并不會特別大。而看起來平平無奇的設置線程優先級的三行代碼,就真的引起了一次線上故障。這也提醒大家,一旦涉及到多線程,就一定要對時序問題特別謹慎。

如果這篇文章對你有幫助,歡迎分享,收藏,點贊!

分享到:
標簽:Android
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定