Android線程相關(guān)

==進(jìn)程和線程==

  • 進(jìn)程:進(jìn)程是資源分配的基本單位。進(jìn)程控制塊 (Process Control Block, PCB) 描述進(jìn)程的基本信息和運(yùn)行狀態(tài),所謂的創(chuàng)建進(jìn)程和撤銷進(jìn)程,都是指對(duì) PCB 的操作。
  • 線程:線程是獨(dú)立調(diào)度的基本單位。一個(gè)進(jìn)程中可以有多個(gè)線程,它們共享進(jìn)程資源。

區(qū)別

  • 擁有資源:進(jìn)程是資源分配的基本單位,但是線程不擁有資源,線程可以訪問隸屬進(jìn)程的資源。
  • 調(diào)度:線程是獨(dú)立調(diào)度的基本單位,在同一進(jìn)程中,線程的切換不會(huì)引起進(jìn)程切換,從一個(gè)進(jìn)程中的線程切換到另一個(gè)進(jìn)程中的線程時(shí),會(huì)引起進(jìn)程切換。
  • 系統(tǒng)開銷:由于創(chuàng)建或撤銷進(jìn)程時(shí),系統(tǒng)都要為之分配或回收資源,如內(nèi)存空間、I/O 設(shè)備等,開銷很大。而線程切換時(shí)只需保存和設(shè)置少量寄存器內(nèi)容,開銷很小。
  • 通信方面:線程間可以通過直接讀寫同一進(jìn)程中的數(shù)據(jù)進(jìn)行通信,但是進(jìn)程通信需要借助 IPC。

線程池的好處? 四種線程池的使用場景,線程池的幾個(gè)參數(shù)的理解?

  • 使用線程池的好處是==減少在創(chuàng)建和銷毀線程上所花的時(shí)間以及系統(tǒng)資源的開銷,解決資源不足的問題==。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或則“過度切換”的問題,歸納總結(jié)就是
  1. 重用存在的線程,減少對(duì)象創(chuàng)建、消亡的開銷,性能佳。
  2. 可有效控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過多資源競爭,避免堵塞。
  3. 提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。
  • ==Android中的線程池都是直接或間接通過配置ThreadPoolExecutor來實(shí)現(xiàn)不同特性的線程池==.Android中最常見的類具有不同特性的線程池分別為:
  1. ==newCachedThreadPool==:只有非核心線程,最大線程數(shù)非常大,所有線程都活動(dòng)時(shí)會(huì)為新任務(wù)創(chuàng)建新線程,否則會(huì)利用空閑線程 (60s空閑時(shí)間,過了就會(huì)被回收,所以線程池中有0個(gè)線程的可能 )來處理任務(wù).

優(yōu)點(diǎn):任何任務(wù)都會(huì)被立即執(zhí)行(任務(wù)隊(duì)列SynchronousQuue相當(dāng)于一個(gè)空集合);比較適合執(zhí)行大量的耗時(shí)較少的任務(wù).

  1. ==newFixedThreadPool==:只有核心線程,并且數(shù)量固定的,所有線程都活動(dòng)時(shí),因?yàn)殛?duì)列沒有限制大小,新任務(wù)會(huì)等待執(zhí)行,當(dāng)線程池空閑時(shí)不會(huì)釋放工作線程,還會(huì)占用一定的系統(tǒng)資源。

優(yōu)點(diǎn):更快的響應(yīng)外界請(qǐng)求

  1. ==newScheduledThreadPool==:核心線程數(shù)固定,非核心線程(閑著沒活干會(huì)被立即回收數(shù))沒有限制.

優(yōu)點(diǎn):執(zhí)行定時(shí)任務(wù)以及有固定周期的重復(fù)任務(wù)

  1. ==newSingleThreadExecutor==:只有一個(gè)核心線程,確保所有的任務(wù)都在同一線程中按序完成

優(yōu)點(diǎn):不需要處理線程同步的問題

ThreadPoolExecutor的工作策略 ?

ThreadPoolExecutor執(zhí)行任務(wù)時(shí)會(huì)遵循如下規(guī)則

  1. 如果線程池中的線程數(shù)量未達(dá)到核心線程的數(shù)量,那么會(huì)直接啟動(dòng)一個(gè)核心線程來執(zhí)行任務(wù)。
  2. 如果線程池中的線程數(shù)量已經(jīng)達(dá)到或則超過核心線程的數(shù)量,那么任務(wù)會(huì)被插入任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行。
  3. 如果在第2點(diǎn)無法將任務(wù)插入到任務(wù)隊(duì)列中,這往往是由于任務(wù)隊(duì)列已滿,這個(gè)時(shí)候如果在線程數(shù)量未達(dá)到線程池規(guī)定的最大值,那么會(huì)立刻啟動(dòng)一個(gè)非核心線程來執(zhí)行任務(wù)。
  4. 如果第3點(diǎn)中線程數(shù)量已經(jīng)達(dá)到線程池規(guī)定的最大值,那么就拒絕執(zhí)行此任務(wù),ThreadPoolExecutor會(huì)調(diào)用RejectedExecutionHandler的rejectedExecution方法來通知調(diào)用者。

Android中還了解哪些方便線程切換的類?

  • ==AsyncTask==:底層封裝了線程池和Handler,便于執(zhí)行后臺(tái)任務(wù)以及在子線程中進(jìn)行UI操作。
  • ==HandlerThread==:一種具有消息循環(huán)的線程,其內(nèi)部可使用Handler。
  • ==IntentService==:是一種異步、會(huì)自動(dòng)停止的服務(wù),內(nèi)部采用HandlerThread。

AsyncTask的原理和問題

原理:

  • AsyncTask中有兩個(gè)線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個(gè)Handler(InternalHandler),其中==線程池SerialExecutor用于任務(wù)的排隊(duì),而線程池THREAD_POOL_EXECUTOR用于真正地執(zhí)行任務(wù),InternalHandler用于將執(zhí)行環(huán)境從線程池切換到主線程。==
  • 注意:sHandler是一個(gè)靜態(tài)的Handler對(duì)象,為了能夠?qū)?zhí)行環(huán)境切換到主線程,這就要求sHandler這個(gè)對(duì)象必須在主線程創(chuàng)建。由于靜態(tài)成員會(huì)在加載類的時(shí)候進(jìn)行初始化,==因此這就變相要求AsyncTask的類必須在主線程中加載==,否則同一個(gè)進(jìn)程中的AsyncTask都將無法正常工作。

問題:

  • 生命周期:AsynTask會(huì)一直執(zhí)行,直到doInBackground()方法執(zhí)行完畢,然后,如果cancel(boolean)被調(diào)用,那么onCancelled(Result result)方法會(huì)被執(zhí)行;否則,執(zhí)行onPostExecute(Result result)方法。==如果我們的Activity銷毀之前,沒有取消AsyncTask,這有可能讓我們的應(yīng)用崩潰==(crash)。因?yàn)樗胍幚淼膙iew已經(jīng)不存在了。所以,我們是必須確保在銷毀活動(dòng)之前取消任務(wù)。總之,我們使用AsyncTask需要確保AsyncTask正確的取消。
  • ==內(nèi)存泄漏==:如果AsyncTask被聲明為Activity的非靜態(tài)內(nèi)部類,那么AsyncTask會(huì)保留一個(gè)對(duì)Activity的引用。如果Activity已經(jīng)被銷毀,AsyncTask的后臺(tái)線程還在執(zhí)行,它將繼續(xù)在內(nèi)存里保留這個(gè)引用,導(dǎo)致Activity無法被回收,引起內(nèi)存泄漏。(所以應(yīng)將AsyncTask聲明為靜態(tài)內(nèi)部類)
  • ==結(jié)果丟失==:屏幕旋轉(zhuǎn)或Activity在后臺(tái)被系統(tǒng)殺掉等情況會(huì)導(dǎo)致Activity的重新創(chuàng)建,之前運(yùn)行的AsyncTask會(huì)持有一個(gè)之前Activity的引用,這個(gè)引用已經(jīng)無效,這時(shí)調(diào)用onPostExecute()再去更新界面將不再生效。

IntentService有什么用 ?

  • IntentService可用于執(zhí)行后臺(tái)耗時(shí)的任務(wù),當(dāng)任務(wù)執(zhí)行完成后會(huì)自動(dòng)停止,同時(shí)由于IntentService是服務(wù)的原因,不同于普通Service,IntentService可自動(dòng)創(chuàng)建子線程來執(zhí)行任務(wù),這導(dǎo)致它的優(yōu)先級(jí)比單純的線程要高,不容易被系統(tǒng)殺死,所以IntentService比較適合執(zhí)行一些高優(yōu)先級(jí)的后臺(tái)任務(wù)。

直接在Activity中創(chuàng)建一個(gè)thread跟在service中創(chuàng)建一個(gè)thread之間的區(qū)別?

在Activity中被創(chuàng)建:該Thread的就是為這個(gè)Activity服務(wù)的,完成這個(gè)特定的Activity交代的任務(wù),主動(dòng)通知該Activity一些消息和事件,==Activity銷毀后,該Thread也沒有存活的意義了。==
在Service中被創(chuàng)建:這是保證最長生命周期的Thread的唯一方式,==只要整個(gè)Service不退出,Thread就可以一直在后臺(tái)執(zhí)行==,一般在Service的onCreate()中創(chuàng)建,在onDestroy()中銷毀。所以,在Service中創(chuàng)建的Thread,適合長期執(zhí)行一些獨(dú)立于APP的后臺(tái)任務(wù),比較常見的就是:在Service中保持與服務(wù)器端的長連接。

Handler、Thread和HandlerThread的差別?

  • Handler:在android中負(fù)責(zé)發(fā)送和處理消息,通過它可以實(shí)現(xiàn)其他支線線程與主線程之間的消息通訊。
  • Thread:Java進(jìn)程中執(zhí)行運(yùn)算的最小單位,亦即執(zhí)行處理機(jī)調(diào)度的基本單位。某一進(jìn)程中一路單獨(dú)運(yùn)行的程序。
  • HandlerThread:一個(gè)繼承自Thread的類HandlerThread,Android中沒有對(duì)Java中的Thread進(jìn)行任何封裝,而是提供了一個(gè)繼承自Thread的類HandlerThread類,這個(gè)類對(duì)Java的Thread做了很多便利的封裝。==HandlerThread繼承于Thread,所以它本質(zhì)就是個(gè)Thread。與普通Thread的差別就在于,它在內(nèi)部直接實(shí)現(xiàn)了Looper的實(shí)現(xiàn),這是Handler消息機(jī)制必不可少的。有了自己的looper,可以讓我們?cè)谧约旱木€程中分發(fā)和處理消息==。如果不用HandlerThread的話,需要手動(dòng)去調(diào)用Looper.prepare()和Looper.loop()這些方法。

ThreadLocal的原理

==ThreadLocal是一個(gè)關(guān)于創(chuàng)建線程局部變量的類==。使用場景如下所示:

  • 實(shí)現(xiàn)單個(gè)線程單例以及單個(gè)線程上下文信息存儲(chǔ),比如交易id等。

  • 實(shí)現(xiàn)線程安全,非線程安全的對(duì)象使用ThreadLocal之后就會(huì)變得線程安全,因?yàn)槊總€(gè)線程都會(huì)有一個(gè)對(duì)應(yīng)的實(shí)例。 承載一些線程相關(guān)的數(shù)據(jù),避免在方法中來回傳遞參數(shù)。

  • 當(dāng)需要使用多線程時(shí),有個(gè)變量恰巧不需要共享,此時(shí)就不必使用synchronized這么麻煩的關(guān)鍵字來鎖住,每個(gè)線程都相當(dāng)于在堆內(nèi)存中開辟一個(gè)空間,線程中帶有對(duì)共享變量的緩沖區(qū),通過緩沖區(qū)將堆內(nèi)存中的共享變量進(jìn)行讀取和操作,==ThreadLocal相當(dāng)于線程內(nèi)的內(nèi)存,一個(gè)局部變量。每次可以對(duì)線程自身的數(shù)據(jù)讀取和操作,并不需要通過緩沖區(qū)與 主內(nèi)存中的變量進(jìn)行交互==。并不會(huì)像synchronized那樣修改主內(nèi)存的數(shù)據(jù),再將主內(nèi)存的數(shù)據(jù)復(fù)制到線程內(nèi)的工作內(nèi)存。==ThreadLocal可以讓線程獨(dú)占資源,存儲(chǔ)于線程內(nèi)部,避免線程堵塞造成CPU吞吐下降==。

  • 在每個(gè)Thread中包含一個(gè)ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的對(duì)象,value是獨(dú)享數(shù)據(jù)。

多線程是否一定會(huì)高效(優(yōu)缺點(diǎn))

  • 多線程的優(yōu)點(diǎn):
  1. 方便高效的內(nèi)存共享——多進(jìn)程下內(nèi)存共享比較不便,且會(huì)抵消掉多進(jìn)程編程的好處
  2. 較輕的上下文切換開銷不用切換地址空間,不用更改CR3寄存器,不用清空TLB
  3. 線程上的任務(wù)執(zhí)行完后自動(dòng)銷毀
  • 多線程的缺點(diǎn):
  1. 開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,每一個(gè)線程都占512KB)
  2. 如果開啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能
  3. 線程越多,cpu在調(diào)用線程上的開銷就越大
    程序設(shè)計(jì)更加復(fù)雜,比如線程間的通信、多線程的數(shù)據(jù)共享。
  • 綜上得出,多線程不一定能提高效率,在內(nèi)存空間緊張的情況下反而是一種負(fù)擔(dān),因此在日常開發(fā)中,應(yīng)盡量:
  1. 不要頻繁創(chuàng)建,銷毀線程,使用線程池
  2. 減少線程間同步和通信(最為關(guān)鍵)
  3. 避免需要頻繁共享寫的數(shù)據(jù)
  4. 合理安排共享數(shù)據(jù)結(jié)構(gòu),避免偽共享(false sharing)
  5. 使用非阻塞數(shù)據(jù)結(jié)構(gòu)/算法
  6. 避免可能產(chǎn)生可伸縮性問題的系統(tǒng)調(diào)用(比如mmap)
  7. 避免產(chǎn)生大量缺頁異常,盡量使用Huge Page
  8. 可以的話使用用戶態(tài)輕量級(jí)線程代替內(nèi)核線程

參考鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容