iOS多線程-思維導(dǎo)圖、面試題及其答案

iOS 多線程完整知識體系

?? 一、多線程思維導(dǎo)圖

多線程
├── 1. 基礎(chǔ)概念
│   ├── 進程(Process)與線程(Thread)
│   ├── 同步(Synchronous) vs 異步(Asynchronous)
│   ├── 串行(Serial) vs 并發(fā)(Concurrent)
│   ├── 線程安全(Thread Safety)與競態(tài)條件(Race Condition)
│   ├── 上下文切換(Context Switch)
│   └── 死鎖(Deadlock)
├── 2. iOS 多線程方案
│   ├── pthread(POSIX線程,C語言,跨平臺,手動管理)
│   ├── NSThread(OC,面向?qū)ο?,手動管理?│   ├── GCD(Grand Central Dispatch,C語言,自動管理,基于隊列)
│   │   ├── 函數(shù)
│   │   │   ├── dispatch_sync(同步派發(fā))
│   │   │   └── dispatch_async(異步派發(fā))
│   │   ├── 隊列
│   │   │   ├── 串行隊列(Serial Queue)
│   │   │   ├── 并發(fā)隊列(Concurrent Queue)
│   │   │   ├── 主隊列(Main Queue)
│   │   │   └── 全局并發(fā)隊列(Global Concurrent Queue)
│   │   ├── 隊列組(Dispatch Group)
│   │   ├── 柵欄(Dispatch Barrier)
│   │   ├── 信號量(Dispatch Semaphore)
│   │   ├── 數(shù)據(jù)源(Dispatch Source)
│   │   └── 死鎖場景(Deadlock Scenarios)
│   └── NSOperationQueue(OC,面向?qū)ο螅贕CD)
│       ├── NSOperation 子類(NSInvocationOperation,NSBlockOperation)
│       ├── 特性:依賴(Dependency)、KVO、取消(Cancel)、最大并發(fā)數(shù)(MaxConcurrentOperationCount)
│       └── 對比 GCD
├── 3. 線程安全與鎖
│   ├── 安全隱患
│   │   ├── 資源共享(Resource Sharing)
│   │   ├── 數(shù)據(jù)錯亂(Data Corruption)
│   │   └── 死鎖(Deadlock)
│   ├── 鎖方案
│   │   ├── OSSpinLock(自旋鎖,已廢棄)
│   │   ├── os_unfair_lock(非公平鎖,替代自旋鎖)
│   │   ├── pthread_mutex(互斥鎖,支持遞歸鎖、條件鎖)
│   │   ├── NSLock / NSRecursiveLock(遞歸鎖)
│   │   ├── NSCondition(條件變量) / NSConditionLock(條件鎖)
│   │   ├── dispatch_semaphore(信號量)
│   │   ├── @synchronized(同步鎖)
│   │   ├── atomic(原子屬性修飾符)
│   │   └── 串行隊列實現(xiàn)同步(Serial Queue for Synchronization)
│   ├── 讀寫安全
│   │   ├── pthread_rwlock(讀寫鎖)
│   │   └── dispatch_barrier_async(異步柵欄)
│   └── 自旋鎖(Spinlock) vs 互斥鎖(Mutex)
│       ├── 自旋鎖:忙等(Busy-wait)、短臨界區(qū)、多核
│       └── 互斥鎖:休眠(Sleep)、長臨界區(qū)、單核
├── 4. 常見問題與場景
│   ├── performSelector 延時與 RunLoop(運行循環(huán))
│   ├── 主隊列死鎖(Main Queue Deadlock)
│   ├── 線程同步經(jīng)典案例:存錢取錢、賣票
│   └── 多讀單寫(Multiple Readers Single Writer)實現(xiàn)
└── 5. 性能與源碼
    ├── 鎖性能對比(os_unfair_lock > dispatch_semaphore > ...)
    ├── GCD 源碼:libdispatch
    ├── objc4 中的 @synchronized 實現(xiàn)
    └── atomic 實現(xiàn):objc-accessors.mm

?二、面試題及答案

下面列出 15 道核心面試題,每道題均包含 專業(yè)答案(按【初級掌握】【中級擴展】【高級深入】分層)和 通俗解釋


1. 你理解的多線程?

【專業(yè)答案】

  • 初級掌握:多線程是指從軟件或硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。在 iOS 中,主線程負責 UI 渲染,耗時操作(網(wǎng)絡(luò)請求、文件讀寫)放在子線程,避免界面卡頓。
  • 中級擴展:多線程的核心目的是充分利用多核 CPU,提高資源利用率。但同時也帶來線程安全問題,如競態(tài)條件、死鎖。iOS 中通過 RunLoop、GCD 等機制管理線程生命周期,開發(fā)者需關(guān)注線程間通信(如 performSelectordispatch_async 回到主線程)。
  • 高級深入:從操作系統(tǒng)層面,線程是 CPU 調(diào)度的最小單位,每個線程有自己的??臻g和寄存器狀態(tài)。iOS 采用 XNU 內(nèi)核,基于 Mach 線程和 POSIX 線程(pthread)實現(xiàn)。GCD 通過線程池管理,避免頻繁創(chuàng)建銷毀線程的開銷,同時利用 workqueue 機制根據(jù)系統(tǒng)負載動態(tài)調(diào)整線程數(shù)。深入理解多線程需要掌握內(nèi)存屏障、指令重排、緩存一致性等底層知識。

【通俗解釋】

多線程就是讓計算機同時干多件事。就像一個人(主線程)做飯,如果還要接電話,就會手忙腳亂;多線程就是多找?guī)讉€人幫忙,各干各的,提高效率。但人多了容易吵架(數(shù)據(jù)錯亂)或互相等死(死鎖),需要一套管理規(guī)則。


2. iOS 的多線程方案有哪幾種?你更傾向于哪一種?

【專業(yè)答案】

  • 初級掌握:四種方案:pthread(C 語言,跨平臺)、NSThread(OC,面向?qū)ο螅CD(C 語言,基于隊列)、NSOperation(OC,基于 GCD)。我傾向使用 GCD,因為它簡單高效,能滿足大部分需求。
  • 中級擴展:選擇依據(jù)場景:
    • GCD:適合輕量級異步任務(wù),如網(wǎng)絡(luò)回調(diào)、數(shù)據(jù)解析。
    • NSOperationQueue:適合復(fù)雜任務(wù)管理,如任務(wù)依賴、取消、最大并發(fā)數(shù)控制。
    • NSThread:需要手動管理線程生命周期,幾乎不用。
    • pthread:底層跨平臺需求,如音視頻 SDK。
      我傾向于 NSOperationQueue,因為它提供更高層的抽象,且易于維護。
  • 高級深入:從性能角度,GCD 基于 libdispatch,是 C 語言實現(xiàn)的,輕量且高效,其底層使用線程池和 workqueue,能根據(jù) CPU 核心數(shù)和負載動態(tài)調(diào)整。NSOperationQueue 是對 GCD 的封裝,增加了 OC 對象開銷,但提供了 KVO、依賴等特性。在需要精細控制時,也可直接使用 pthread。蘋果官方推薦優(yōu)先使用 GCD 和 NSOperation,避免直接操作線程。我還研究過 GCD 的源碼,其隊列、組、信號量的實現(xiàn)涉及雙向鏈表、原子操作和條件變量。

【通俗解釋】

就像干活有不同的工具:GCD 像一把瑞士軍刀,簡單好用;NSOperationQueue 像工具箱,功能更多(可以設(shè)置任務(wù)依賴、取消等)。簡單任務(wù)用 GCD,復(fù)雜任務(wù)用 NSOperationQueue。


3. GCD 的隊列類型有哪些?

【專業(yè)答案】

  • 初級掌握:兩種:串行隊列(Serial Dispatch Queue)和并發(fā)隊列(Concurrent Dispatch Queue)。還有系統(tǒng)提供的主隊列(主線程串行)和全局并發(fā)隊列。
  • 中級擴展:隊列分為:
    • 主隊列:綁定主線程,串行。
    • 自定義串行隊列:創(chuàng)建一個新線程(或復(fù)用線程池中的線程)串行執(zhí)行。
    • 全局并發(fā)隊列:系統(tǒng)提供的并發(fā)隊列,有四個優(yōu)先級(對應(yīng) QOS)。
    • 自定義并發(fā)隊列:自己創(chuàng)建的并發(fā)隊列,可以設(shè)置優(yōu)先級。
      注意:dispatch_sync 向當前串行隊列提交任務(wù)會導(dǎo)致死鎖。
  • 高級深入:GCD 隊列底層是一個 dispatch_queue_s 結(jié)構(gòu)體,包含隊列類型、優(yōu)先級、掛載的線程池等。串行隊列底層綁定一個 pthread_workqueue,而并發(fā)隊列使用線程池,任務(wù)通過 dispatch_async 提交后,由 libdispatch 的 worker 線程執(zhí)行。全局并發(fā)隊列本質(zhì)上是系統(tǒng)維護的幾個靜態(tài)隊列,優(yōu)先級對應(yīng) QOS_CLASS。深入理解隊列需要研究 dispatch_async 的入隊和喚醒機制,涉及系統(tǒng)調(diào)用和 workqueue 的負載均衡。

【通俗解釋】

隊列就像任務(wù)管道:主隊列是給主線程用的(只有一條路),串行隊列也是一條路,一個一個來;并發(fā)隊列是多個路,可以同時走;全局隊列是公共道路。


4. OperationQueue 和 GCD 的區(qū)別,各自的優(yōu)勢?

【專業(yè)答案】

  • 初級掌握
    • GCD:基于 C,輕量,適合簡單異步任務(wù)。
    • NSOperationQueue:基于 GCD,面向?qū)ο?,支持任?wù)依賴、取消、狀態(tài)監(jiān)控。
  • 中級擴展
    • GCD 優(yōu)勢:語法簡潔,性能高,使用 block,適合快速開發(fā)。
    • NSOperationQueue 優(yōu)勢:可設(shè)置最大并發(fā)數(shù)、任務(wù)依賴(addDependency)、暫停/恢復(fù)(suspended)、KVO 監(jiān)聽狀態(tài),支持自定義 NSOperation 子類。
      選擇:如果需要精細控制任務(wù)流程,用 NSOperationQueue;否則用 GCD。
  • 高級深入:NSOperationQueue 底層使用 GCD 的隊列和信號量實現(xiàn),但其抽象層提供了更豐富的功能。例如,任務(wù)依賴通過維護一個等待隊列和計數(shù)器實現(xiàn);取消操作通過原子標志位和 KVO 觸發(fā)。GCD 則更接近底層,沒有這些高層抽象,但可以自己用 dispatch_group、dispatch_semaphore 模擬。性能上,GCD 略勝一籌,因為 NSOperationQueue 增加了 Objective-C 消息轉(zhuǎn)發(fā)和 KVO 開銷。但在實際應(yīng)用中,除非是極端性能要求,否則差異可忽略。

【通俗解釋】

GCD 寫起來快,像一把快刀;NSOperationQueue 像一把多功能軍刀,可以隨時取消任務(wù),讓任務(wù)排隊等,適合復(fù)雜場景。


5. 線程安全的處理手段有哪些?

【專業(yè)答案】

  • 初級掌握:加鎖(如 @synchronized、NSLock)、使用串行隊列、atomic 屬性修飾符。
  • 中級擴展:常見手段:
    • :互斥鎖(pthread_mutex、NSLock)、自旋鎖(os_unfair_lock)、遞歸鎖、條件鎖、讀寫鎖。
    • GCD:串行隊列、信號量(dispatch_semaphore)、柵欄(dispatch_barrier_async)。
    • 其他:atomic(僅保證 setter/getter 原子性)、不可變對象(如 NSString)、線程本地存儲(Thread Local Storage)。
      需根據(jù)場景選擇合適方案,避免死鎖和性能瓶頸。
  • 高級深入:深入理解線程安全需考慮硬件層面(緩存一致性、內(nèi)存屏障)和編譯器層面(指令重排)。iOS 中,atomic 修飾符在底層通過 spinlock(舊版)或 os_unfair_lock(新版)實現(xiàn),但只保證讀寫的原子性,不保證業(yè)務(wù)邏輯安全。真正的線程安全需要從設(shè)計上避免共享可變狀態(tài),多用值類型(Swift)或不可變對象。此外,無鎖編程(Lock-Free)利用 CAS 原子操作實現(xiàn)高性能同步,但難度極高,需謹慎。

【通俗解釋】

給共享資源加把鎖,一次只讓一個人訪問。鎖的種類很多,各有特點。還可以用 GCD 的隊列來串行化訪問,或者用讀寫鎖實現(xiàn)多讀單寫。


6. 你了解的鎖有哪些?請對比自旋鎖和互斥鎖,并說明使用注意。

【專業(yè)答案】

  • 初級掌握:鎖有 OSSpinLock、os_unfair_lockpthread_mutex、NSLock@synchronized 等。自旋鎖會一直占用 CPU 等待,互斥鎖會讓線程休眠。使用注意避免死鎖。
  • 中級擴展
    • 自旋鎖:適合臨界區(qū)短、多核環(huán)境,避免線程切換開銷。但 OSSpinLock 已廢棄,因優(yōu)先級反轉(zhuǎn)問題,改用 os_unfair_lock(實際是互斥鎖)。
    • 互斥鎖:適合臨界區(qū)長、單核環(huán)境,線程休眠不占 CPU。
    • 注意:鎖的粒度要小,避免在加鎖時調(diào)用外部可能加鎖的代碼;遞歸鎖用于遞歸函數(shù);使用 @synchronized 時要注意對象不能為 nil。
  • 高級深入
    • OSSpinLock:基于忙等,無系統(tǒng)調(diào)用,但高優(yōu)先級線程等待低優(yōu)先級線程釋放鎖時,低優(yōu)先級無法調(diào)度,導(dǎo)致優(yōu)先級反轉(zhuǎn)。iOS 10 后用 os_unfair_lock 替代,它基于 mach semaphore,線程會休眠。
    • pthread_mutex:支持普通、遞歸、條件等多種類型,性能高,可跨平臺。底層使用 futex(Linux)或 ulock(iOS)實現(xiàn)快速用戶態(tài)鎖。
    • dispatch_semaphore:信號量,初始值設(shè)為 1 可作互斥鎖,底層基于條件變量和原子操作。
    • 性能對比os_unfair_lock 最快,其次是 dispatch_semaphore,然后是 pthread_mutex,最后是 @synchronized(因需查表、異常處理)。
    • 注意:鎖的使用要結(jié)合內(nèi)存屏障防止指令重排;在 Swift 中建議使用 os_unfair_lockDispatchQueue 串行同步。

【通俗解釋】

自旋鎖就像你在門口一直敲門,適合快速操作;互斥鎖是先去睡覺,有人叫你再起來,適合慢操作。用鎖時要小心,別把自己鎖死(死鎖)。不同鎖的速度也不同,os_unfair_lock 最快。


7. 如何實現(xiàn)多讀單寫(讀寫安全)?

【專業(yè)答案】

  • 初級掌握:可以使用 dispatch_barrier_async 實現(xiàn),寫操作使用柵欄,讀操作使用異步并發(fā)。
  • 中級擴展:兩種主流方案:
    • pthread_rwlock:讀寫鎖,讀加讀鎖,寫加寫鎖,支持多讀單寫。
    • dispatch_barrier_async:在自定義并發(fā)隊列中,寫操作提交柵欄任務(wù),讀操作提交普通異步任務(wù),利用柵欄保證寫?yīng)氄肌?br> 注意:柵欄必須用在自定義并發(fā)隊列,全局隊列無效。
  • 高級深入pthread_rwlock 底層基于條件變量和互斥鎖實現(xiàn),讀鎖可重入,寫鎖優(yōu)先級可能提升以避免寫?zhàn)囸I。dispatch_barrier_async 的底層機制:在并發(fā)隊列中,柵欄任務(wù)之前的任務(wù)全部執(zhí)行完畢后,柵欄任務(wù)單獨執(zhí)行,之后的任務(wù)才繼續(xù)并發(fā)。其實現(xiàn)依賴于隊列的掛起和喚醒機制。性能上,pthread_rwlock 在大量讀場景下表現(xiàn)更好,柵欄在寫操作較少時開銷較低。還需考慮公平性,避免寫線程長時間等待。

【通俗解釋】

讀可以同時多人,寫只能一個人,寫的時候不能讀。pthread_rwlock 是專門的讀寫鎖,柵欄是利用 GCD 的特性實現(xiàn)的。


8. 什么是上下文切換?它對性能有什么影響?

【專業(yè)答案】

  • 初級掌握:上下文切換是指 CPU 從一個線程/進程切換到另一個線程/進程時,保存當前狀態(tài)(上下文)并加載新狀態(tài)的過程。頻繁切換會消耗 CPU 時間,降低性能。
  • 中級擴展:上下文切換涉及寄存器狀態(tài)、程序計數(shù)器、棧指針等的保存與恢復(fù),以及 TLB 刷新、緩存失效等。在 iOS 中,線程切換開銷約為幾微秒到幾十微秒。過多的線程或鎖競爭會導(dǎo)致頻繁切換,成為性能瓶頸。使用 GCD 的線程池可減少切換,因為任務(wù)在同一線程上連續(xù)執(zhí)行。
  • 高級深入:上下文切換分為用戶態(tài)切換(如協(xié)程)和內(nèi)核態(tài)切換(線程切換)。內(nèi)核態(tài)切換需要陷入內(nèi)核,執(zhí)行調(diào)度算法,開銷更大。iOS 基于 Mach 內(nèi)核,使用 workqueue 機制,線程切換涉及 mach_msg、thread_switch 等系統(tǒng)調(diào)用。深入優(yōu)化需減少鎖競爭、使用無鎖編程、合理設(shè)置線程優(yōu)先級,避免優(yōu)先級反轉(zhuǎn)導(dǎo)致額外切換。

【通俗解釋】

就像你正在看書,突然要去接電話,你需要記住看到哪一頁(保存狀態(tài)),接完電話再翻到那頁(恢復(fù)狀態(tài))。頻繁接電話就會浪費很多時間在翻書上。多線程切換也是一樣,切換太多會拖慢速度。


9. 什么是 dispatch_source?有哪些應(yīng)用場景?

【專業(yè)答案】

  • 初級掌握dispatch_source 是 GCD 提供的一種機制,用于監(jiān)聽底層系統(tǒng)事件(如定時器、文件描述符、信號等)并提交處理 block。常用場景:定時器、監(jiān)控文件變化、處理信號。
  • 中級擴展dispatch_sourceNSTimer 更精準(不受 RunLoop 模式影響),且支持取消、暫停、合并事件。例如,用 dispatch_source 創(chuàng)建定時器可實現(xiàn)后臺持續(xù)任務(wù);用 DISPATCH_SOURCE_TYPE_VNODE 監(jiān)控文件寫入。它基于事件驅(qū)動,效率高。
  • 高級深入dispatch_source 底層使用內(nèi)核事件隊列(kqueue)或 Mach 端口,將事件轉(zhuǎn)換為 GCD 任務(wù)提交到指定隊列。其事件處理支持合并(如 DISPATCH_SOURCE_TYPE_DATA_ADD),可減少回調(diào)次數(shù)。源碼中,dispatch_source 通過 kevent 注冊事件,由 _dispatch_kevent_work 線程喚醒。在 iOS 中,常用 dispatch_source 實現(xiàn)高性能定時器,避免 NSTimer 對 RunLoop 的依賴。

【通俗解釋】

dispatch_source 就像一個小哨兵,專門盯著某些事情發(fā)生(比如時間到了、文件改了),然后通知你去做事。它比普通的定時器更可靠,不會因為你在刷朋友圈就耽誤了。


10. NSCondition 和 NSConditionLock 的區(qū)別與使用?

【專業(yè)答案】

  • 初級掌握NSCondition 用于線程間的條件通知,可以等待某個條件成立再繼續(xù)執(zhí)行;NSConditionLock 是帶有特定條件值的鎖,只有條件匹配時才能獲得鎖。兩者都用于多線程同步。
  • 中級擴展NSCondition 封裝了 pthread_cond_tpthread_mutex_t,提供 waitsignal、broadcast 方法,適合生產(chǎn)者-消費者模式。NSConditionLock 封裝了 NSCondition,并增加了一個整型條件值,只有 lockWhenCondition: 時條件匹配才能加鎖,更適用于狀態(tài)機場景。使用時注意 wait 前必須加鎖,signal 后要解鎖。
  • 高級深入NSCondition 底層是 pthread 條件變量,wait 內(nèi)部調(diào)用 pthread_cond_wait,會原子性地釋放鎖并阻塞線程,被 signal 喚醒后重新獲取鎖。NSConditionLock 在 condition 上維護一個整數(shù)值,加鎖時檢查當前值,不匹配則等待。其源碼可在 GNUstep 中找到。性能上,兩者接近,但 NSConditionLock 因增加條件檢查略慢。實際開發(fā)中,可用 dispatch_semaphore 或串行隊列替代。

【通俗解釋】

NSCondition 像是一個廣播站,線程可以等待特定消息;NSConditionLock 像是一個密碼鎖,只有密碼對了才能開門。


11. atomic 關(guān)鍵字的底層實現(xiàn)?它能保證絕對線程安全嗎?

【專業(yè)答案】

  • 初級掌握:atomic 是屬性修飾符,保證屬性的 setter/getter 方法原子性,即同時讀寫不會被中斷。但并不能保證整個對象的線程安全。
  • 中級擴展:atomic 底層通過鎖(舊版使用 spinlock,新版使用 os_unfair_lock)在 setter/getter 內(nèi)部加鎖實現(xiàn)。例如,objc_setPropertyobjc_getProperty 內(nèi)部會加鎖。它只保證單個屬性的讀寫原子性,但不保證復(fù)合操作(如先讀后改)的安全,且性能低于 nonatomic。
  • 高級深入:源碼位于 objc-accessors.mm,atomic 屬性的 setter 調(diào)用 reallySetProperty,內(nèi)部使用 spinlock_t(本質(zhì)是 os_unfair_lock)加鎖。注意,atomic 只防止讀寫同時發(fā)生,但不阻止多個線程同時調(diào)用不同方法導(dǎo)致的數(shù)據(jù)競爭。真正的線程安全需要更高級的同步機制。此外,atomic 在 64 位平臺上對某些類型使用原子指令(如 atomic_load)優(yōu)化,但本質(zhì)上仍有限。

【通俗解釋】

atomic 就像給屬性加了一把小鎖,保證你拿的時候不會被人改,但如果你先拿了再去做別的事,中間還是可能出問題。所以它不是萬能藥。


12. 請簡述 GCD 的線程池管理機制(基于源碼理解)。

【專業(yè)答案】

  • 初級掌握:GCD 內(nèi)部維護一個線程池,根據(jù)任務(wù)數(shù)量和系統(tǒng)負載動態(tài)創(chuàng)建和回收線程,避免頻繁創(chuàng)建線程的開銷。
  • 中級擴展:GCD 的線程池稱為 "workqueue" 或 "root queue"。全局并發(fā)隊列對應(yīng)幾個優(yōu)先級的 workqueue,自定義隊列會掛載到這些 workqueue 上。當任務(wù)提交時,GCD 根據(jù)隊列類型和優(yōu)先級,從線程池中喚醒或創(chuàng)建線程執(zhí)行。線程閑置一段時間后會被回收。通過 dispatch_async 提交的任務(wù),底層調(diào)用 _dispatch_worker_thread,最終由 pthread 創(chuàng)建。
  • 高級深入:深入 libdispatch 源碼:dispatch_queue_t 對應(yīng) struct dispatch_queue_s,內(nèi)部有 dq_statedq_running 等字段。提交任務(wù)時,通過 _dispatch_queue_wakeup 喚醒線程。線程池由 _dispatch_worker_threads 管理,使用 _pthread_workqueue_addthreads 創(chuàng)建新線程。GCD 采用 overcommit 策略,可根據(jù) CPU 負載動態(tài)調(diào)整線程數(shù),優(yōu)先級通過 QOS 映射。研究源碼可發(fā)現(xiàn),GCD 的線程管理涉及 Mach 內(nèi)核的 workqueue 機制,高效且省電。

【通俗解釋】

GCD 就像一個調(diào)度中心,手底下有一群工人(線程)。任務(wù)來了,它會安排空閑的工人去做;工人不夠就招新;工人閑著太久就讓他們回家休息。這樣既快又不浪費資源。


13. @synchronized 的底層實現(xiàn)原理?優(yōu)缺點?

【專業(yè)答案】

  • 初級掌握@synchronized 是 OC 提供的便捷加鎖語法,使用對象作為鎖,保證同一時刻只有一個線程能執(zhí)行大括號內(nèi)的代碼。
  • 中級擴展@synchronized 底層基于 pthread_mutex 遞歸鎖實現(xiàn)。每個對象關(guān)聯(lián)一個遞歸鎖,存儲在全局的 SideTable 中(通過對象的指針哈希映射)。當?shù)谝淮问褂脮r創(chuàng)建鎖,之后復(fù)用。優(yōu)點是語法簡潔,支持遞歸;缺點是性能較差(需查表、異常處理),且容易因濫用導(dǎo)致死鎖。
  • 高級深入:源碼位于 objc-sync.mm@synchronized(obj) 展開為 objc_sync_enter(obj)objc_sync_exit(obj)。內(nèi)部通過 id2data(obj) 找到對應(yīng)的 SyncData,SyncData 包含遞歸鎖和線程計數(shù)。支持嵌套加鎖,因為遞歸鎖。缺點:鎖的全局哈希表可能沖突,且有異常保護開銷。蘋果建議在性能敏感時使用其他鎖。

【通俗解釋】

@synchronized 就像給代碼塊貼了一個標簽,標簽對應(yīng)的對象是誰,誰就是鑰匙。同一時間只有拿著鑰匙的人能進去。它很方便,但人多的時候有點慢。


14. 什么是死鎖?如何避免死鎖?

【專業(yè)答案】

  • 初級掌握:死鎖是指兩個或多個線程互相等待對方釋放資源,導(dǎo)致所有線程都無法繼續(xù)執(zhí)行。例如,主隊列同步任務(wù)就會死鎖。
  • 中級擴展:死鎖的四個必要條件:互斥、持有并等待、不可剝奪、循環(huán)等待。避免方法:破壞任一條件,如使用 tryLock、按相同順序加鎖、使用鎖超時、減少鎖粒度。在 iOS 中,避免在串行隊列中調(diào)用同步任務(wù),使用異步設(shè)計。
  • 高級深入:死鎖不僅發(fā)生在鎖上,也可能發(fā)生在 GCD 隊列(如 dispatch_sync 向當前隊列提交)。更深層,系統(tǒng)級死鎖涉及資源分配圖。檢測死鎖可使用 Instruments 的 Thread State 工具。設(shè)計時采用 "鎖排序" 或 "資源分級" 策略。對于復(fù)雜系統(tǒng),可用事務(wù)機制或死鎖檢測算法(如銀行家算法)預(yù)防。

【通俗解釋】

死鎖就像兩個人互相擋住對方的出路,誰也別想走。要避免這種情況,就要事先約定好誰先走,或者讓一個人主動讓一下。


15. 什么是競態(tài)條件?舉例說明并給出解決方案。

【專業(yè)答案】

  • 初級掌握:競態(tài)條件(Race Condition)是指多個線程同時訪問共享數(shù)據(jù),且結(jié)果依賴于線程執(zhí)行順序,導(dǎo)致數(shù)據(jù)不一致。例如,兩個線程同時給同一個變量 +1,可能只加了一次。
  • 中級擴展:經(jīng)典例子:多線程賣票,不加鎖會導(dǎo)致超賣。解決方案:加鎖(如 @synchronized)、使用串行隊列、原子操作。注意,競態(tài)條件不僅發(fā)生在寫操作,讀-改-寫操作也可能出現(xiàn)。
  • 高級深入:競態(tài)條件本質(zhì)是操作的非原子性。編譯器優(yōu)化和 CPU 亂序執(zhí)行也可能引入競態(tài)。修復(fù)方法:使用原子操作(如 OSAtomicAdd32)、內(nèi)存屏障、鎖或無鎖數(shù)據(jù)結(jié)構(gòu)。在 Swift 中,可使用 actor(Swift 5.5+)隔離可變狀態(tài)。底層需關(guān)注內(nèi)存模型,避免重排。

【通俗解釋】

競態(tài)條件就像兩個人同時搶一個麥克風,結(jié)果誰都沒拿到,或者同時講話亂套了。解決辦法是排隊(加鎖)或者指定一個人拿(原子操作)。


?? 三、初中高工程師回答指南

初級工程師

  • 特點:掌握基本概念,能使用簡單 API,理解常見場景。
  • 準備方向
    • 熟悉 GCD 的基本使用:async/sync,主隊列/全局隊列。
    • 知道鎖的幾種類型(@synchronized, NSLock)。
    • 能解釋死鎖的簡單例子(如主隊列 sync)。
  • 回答技巧
    • 用生活中的例子類比,讓面試官覺得你有理解。
    • 遇到不會的,坦誠并表明自己會繼續(xù)學習。

中級工程師

  • 特點:能根據(jù)場景選擇合適方案,理解原理和性能差異,能處理復(fù)雜問題。
  • 準備方向
    • 深入理解 GCD 隊列類型、組、信號量、柵欄。
    • 掌握 NSOperationQueue 的高級特性(依賴、KVO、取消)。
    • 了解各種鎖的適用場景和性能排序。
    • 熟悉常見的線程安全設(shè)計模式(如多讀單寫)。
  • 回答技巧
    • 對比不同方案的優(yōu)缺點,給出選擇依據(jù)。
    • 結(jié)合項目經(jīng)驗,說明解決過的問題(如用柵欄實現(xiàn)數(shù)據(jù)安全)。
    • 能畫圖或口述代碼示例。

高級工程師

  • 特點:理解底層實現(xiàn),能優(yōu)化性能,解決疑難雜癥,有源碼閱讀能力。
  • 準備方向
    • 研究 GCD 源碼(libdispatch),了解隊列、線程池、workqueue。
    • 理解鎖的底層實現(xiàn)(futex, ulock, mach semaphore)。
    • 掌握無鎖編程、內(nèi)存屏障、指令重排等底層知識。
    • 能設(shè)計高性能線程安全組件(如讀寫鎖封裝)。
  • 回答技巧
    • 深入原理,如提到 "dispatch_async 底層是調(diào)用 _dispatch_worker_thread 喚醒線程"。
    • 分析性能瓶頸,給出優(yōu)化建議(如鎖粒度、避免優(yōu)先級反轉(zhuǎn))。
    • 引用蘋果官方文檔或源碼注釋,展示專業(yè)性。

?? 四、英文術(shù)語速查表

英文術(shù)語 中文解釋 簡要說明
Thread 線程 CPU 調(diào)度的最小單位
Process 進程 資源分配的基本單位,包含多個線程
Concurrency 并發(fā) 多個任務(wù)在同一個時間段內(nèi)交替執(zhí)行
Parallelism 并行 多個任務(wù)在同一時刻同時執(zhí)行(多核)
Synchronous 同步 等待任務(wù)執(zhí)行完成才返回
Asynchronous 異步 不等待任務(wù)執(zhí)行,立即返回
Serial Queue 串行隊列 任務(wù)一個接一個執(zhí)行
Concurrent Queue 并發(fā)隊列 任務(wù)可以同時執(zhí)行
Deadlock 死鎖 兩個或多個線程互相等待對方釋放資源
Race Condition 競態(tài)條件 多個線程訪問共享數(shù)據(jù)導(dǎo)致結(jié)果不確定
Thread Safety 線程安全 多個線程同時訪問不會導(dǎo)致數(shù)據(jù)錯亂
Mutex 互斥鎖 一次只有一個線程進入臨界區(qū)
Spinlock 自旋鎖 線程忙等,不進入休眠
Semaphore 信號量 控制并發(fā)訪問數(shù)量的同步原語
Read-Write Lock 讀寫鎖 多讀單寫,提高讀并發(fā)
Atomic 原子操作 不可被中斷的操作,通常用于屬性修飾
Barrier 柵欄 在并發(fā)隊列中確保任務(wù)順序
Dispatch Group 調(diào)度組 監(jiān)控一組任務(wù)的完成
RunLoop 運行循環(huán) 處理事件和定時器的線程循環(huán)
Thread Pool 線程池 一組預(yù)先創(chuàng)建的線程,用于執(zhí)行任務(wù)
Workqueue 工作隊列 內(nèi)核級線程管理機制,GCD 底層使用
Priority Inversion 優(yōu)先級反轉(zhuǎn) 高優(yōu)先級線程等待低優(yōu)先級線程釋放鎖
Context Switch 上下文切換 CPU 從一個線程切換到另一個線程
Memory Barrier 內(nèi)存屏障 防止指令重排,保證內(nèi)存操作順序

以上是 iOS 多線程完整知識體系,覆蓋了概念、方案、鎖、常見問題、源碼和面試準備??勺鳛閷W習、復(fù)習和面試的系統(tǒng)性資料。如有任何修改或補充需求,請隨時告知。

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

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

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