主頁 > 移動端開發 > 多執行緒筆記

多執行緒筆記

2022-03-19 08:59:17 移動端開發

Thread

執行緒狀態:新建(new),就緒(start),運行(run),阻塞,死亡

start 方法內部呼叫了 run 方法,start 會開啟執行緒,run 只是內部方法;

sleep 會占用鎖,休眠時間到重新運行,wait 會釋放鎖;

stop 停止執行緒比較暴力,對鎖的物件進行強制解鎖,執行緒資源因此得不到正常釋放;

interrupt 不會立馬停止執行緒,只能中斷阻塞狀態的執行緒,可以捕獲到一個例外來處理,加上標識判斷是否中斷;

join 等待該執行緒完成后,才能繼續用下運行;

yield 執行緒讓步,讓自己或者其他執行緒運行,并不能保證其它執行緒就一定能獲得執行權;

wait 進入阻塞狀態,釋放鎖,需要在synchronized使用(獲取鎖后);

notify 喚起執行緒(隨機),notifyAll喚起所有執行緒,釋放鎖,需要在synchronized使用(獲取鎖后),呼叫notify和wait的必須是作用同一個物件;

創建方式

//第一種
new Thread().start();
//第二種
new Thread(Runnable實作類).start();

ThreadLocal

執行緒區域變數,為執行緒提供變數副本,每個執行緒改變副本后不會對其它執行緒造成影響,

內部通過 ThreadLocalMap 來存盤值,每個Thread類里面會有一個 ThreadLocalMap 內部變數,可以直接使用,而 ThreadLocalMap 內部使用陣列來存盤,

public class ThreadLocal<T> {

    static class ThreadLocalMap {

      //The table, resized as necessary. table.length MUST always be a power of two.
      private Enrty[] table;
      static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
        //key為ThreadLocal當前物件,value就是我們存入的值
        Entry(ThreadLocal<?> k, Object v) {
          super(k);
          value = v;
        }
    }
  }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
}    

public class Thread implements Runnable {
  /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

存盤

想要存入的資料實際上并沒有存到 ThreadLocal 中去,而是以這個 ThreadLocal 實體作為key存到了當前執行緒中的一個Map(沒有鏈表結構)中去了;

如果發生hash沖突,會線性向后查找,一直找到 Entry 為 null 的時候添加,key相同則直接更新值;

程序碰到回收的過期資料,會進行探測清理操作(replaceStaleEntry()),執行方法遍歷陣列,直到碰到null停止探測,然后將過期資料清空,清空后把后面的資料往前移,提高后面查詢效率,

查詢

線性查詢,判斷hash值,如果存在相同hash值(發生過hash碰撞),則判斷是否為相同key,如果不是繼續往后迭代查找,如果一致直接回傳value;

查詢也會觸發探測清理操作,

GC回收

ThreadLocalMap 內部使用 Entry extends ThreadLocal 弱參考的陣列來存盤,但是value依然會導致殘留,根本解決方案有兩種

1.需要remove這個Entry;

2.停止當前執行緒(map也會跟著gc);

第二種方案不太好控制,最好使用第一種,

使用弱參考的原因

ThreadLocalMap 自己有探測清理操作,所以只要當前執行緒在運行,呼叫查詢增刪方法也會把value洗掉,避免記憶體泄漏OOM

Handler- 執行緒通訊工具

Handler

Handler 通過 Looper 的 prepare 方法創建Looper物件,存放在 ThreadLocal 中,保證每個執行緒的Looper是獨立的;

Looper 會持有當前執行緒的參考,并且創建一個 MessageQueue 佇列存放訊息 Message;

佇列 MessageQueue  為單向鏈表(增刪效率高),按 Message 中的 when(處理時間)欄位排序;

Handler 負責發送和處理訊息,在發送訊息( enqueueMessage() )的時候會把自己賦值給 Message 中的 target 欄位,在把 Message放入 MessageQueue 中,

Handler 將訊息 message 發送到佇列,Looper會呼叫loop方法開啟一個死回圈,讀取佇列訊息,最后拿到 Handler 實體(target),然后通過 target 呼叫 dispatchMessage 分發到 Handler 所在執行緒中的 callback 中,

一個執行緒只有一個 Looper(包含訊息佇列),可以有多個 Handler,

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);//post Runnable
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

記憶體泄漏

一般發生在延遲訊息中,關閉Activity后延遲訊息還未發出,MessageQueue就會持有這個Message的參考,而Message的target又持有Handler,Handler如果是內部類,會持有Activity,從而導致Activity無法被回收,引發OOM,

Handler 是內部類,持有 Activity 參考,Activity 被 finish 后 Handler 任務又沒處理完,會導致 Activity 無法被回收,需要靜態化處理,在呼叫 removeCallbacksAndMessages 清空訊息佇列,

Looper

Android啟動時就會呼叫 prepare 方法系結 Looper 物件,事件都在 Looper 的控制下,ActivityThread的main方法主要就是做訊息回圈,如果回圈停止,應用也會停止,

public static void main(String[] args) {
    Looper.prepareMainLooper();
    ...
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

 

public static void loop() {
    final Looper me = myLooper();
    ...
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        Message msg = queue.next(); // 獲取Message物件,
        ...
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            ...
        }
        ...
    }
}    

MessageQueue

單鏈表結構,時間順序(when欄位),沒有訊息時,會進入阻塞狀態,此時主執行緒會釋放CPU資源進入休眠狀態,直到下個訊息過來或者事務發生,才會喚醒主執行緒作業;

如果是帶延遲的 message,根據時間順序插入到 MessageQueue,然后入阻塞狀態,如果佇列前面有訊息,會喚醒處理,

屏障訊息

擋住普通訊息以此來保證異步訊息的優先處理,屏障訊息和普通訊息的區別在于屏障訊息沒有 tartget 欄位,同樣按時間 when 排序,擋住它后面的同步訊息的分發;

postSyncBarrier 回傳 int 型別,通過這個數值可以撤銷屏障訊息,并且是私有方法,無法喚醒佇列作業,

message

一般通過 obtain 方法創建物件,會從訊息池中獲取物件,重新賦值然后回傳,避免多次創建物件,并且 obtain 方法有同步鎖,如果池子中沒有物件則new新物件,

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

AsyncTask(Android11將移除該庫,推薦用執行緒池代替)

簡化版異步任務,本質是對 Handler+Executors 的封裝,內部維護一個長度MAX的佇列;

在任務并不會立馬將任務提交給執行緒池,而是等待上一個任務完成后在提交,達到串行的目的,任務完成后利用 Handler 發送訊息,

AsyncTask 被宣告為Activity的非靜態內部類時,會持有參考,容易造成記憶體泄漏,

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

執行緒池 - ExecutorService

Java提供了 Executors 工廠,方便開發者創建,所有的方法回傳的都是ThreadPoolExecutorScheduledThreadPoolExecutor這兩個類的實體,

newCachedThreadPool

可快取執行緒池,如果超過執行緒池長度則回收空閑執行緒,若無可回收,則新建執行緒,因為允許創建的執行緒數量為MAX,執行緒過多可能會導致OOM,

newFixedThreadPool

固定長度執行緒池,可控制最大并發數,超出的執行緒會在佇列中等待,但是 LinkedBlockingQueue 佇列長度是MAX,任務多時會存在OOM,

newScheduledThreadPool

 定時執行緒池,支持定時及周期性的任務執行,但是 LinkedBlockingQueue 佇列長度是MAX,任務多時會存在OOM,

newSingleThreadExecutor

單執行緒的執行緒池,保證所有任務按照指定順序執行,但是 LinkedBlockingQueue 佇列長度是MAX,可能導致OOM,

 

阿里手冊推薦:一般使用自定義執行緒池 ThreadPoolExecutor 創建,可以更加清楚的知道執行緒池的運行規則,精確控制池子粒度,工廠的創建也是走的這套流程,

執行緒個數不是越多越好,執行緒多了切換背景關系時間變多,反而是負擔,

背景關系切換:執行緒呼叫CPU處理任務,但是CPU內核個數比較少,執行緒呼叫完會保存狀態切換到下一個執行緒,很消耗時間,

 

CPU任務:一般記憶體資料計算型任務,主要消耗 CPU 資源,可以將執行緒數設定為 CPU 核心數+1,CPU任務背景關系切換比較頻繁,留出一個內核來協調其余因為頻繁帶來暫停中斷等任務,

IO任務:網路,檔案讀取等,io讀取比較耗時,處理io時CPU是不占用的,所以可以配置多一些,一般是兩倍內核數執行緒,

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/445560.html

標籤:其他

上一篇:春天健身正當時,送你一份人體健康檢測指南

下一篇:鴻蒙手表定位功能Demo體驗,適用兒童、老年和外出旅游安全市場

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more