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)注線程間通信(如
performSelector、dispatch_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_lock、pthread_mutex、NSLock、@synchronized等。自旋鎖會一直占用 CPU 等待,互斥鎖會讓線程休眠。使用注意避免死鎖。 -
中級擴展:
-
自旋鎖:適合臨界區(qū)短、多核環(huán)境,避免線程切換開銷。但
OSSpinLock已廢棄,因優(yōu)先級反轉(zhuǎn)問題,改用os_unfair_lock(實際是互斥鎖)。 - 互斥鎖:適合臨界區(qū)長、單核環(huán)境,線程休眠不占 CPU。
-
注意:鎖的粒度要小,避免在加鎖時調(diào)用外部可能加鎖的代碼;遞歸鎖用于遞歸函數(shù);使用
@synchronized時要注意對象不能為 nil。
-
自旋鎖:適合臨界區(qū)短、多核環(huán)境,避免線程切換開銷。但
-
高級深入:
-
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_lock或DispatchQueue串行同步。
-
OSSpinLock:基于忙等,無系統(tǒng)調(diào)用,但高優(yōu)先級線程等待低優(yōu)先級線程釋放鎖時,低優(yōu)先級無法調(diào)度,導(dǎo)致優(yōu)先級反轉(zhuǎn)。iOS 10 后用
【通俗解釋】
自旋鎖就像你在門口一直敲門,適合快速操作;互斥鎖是先去睡覺,有人叫你再起來,適合慢操作。用鎖時要小心,別把自己鎖死(死鎖)。不同鎖的速度也不同,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_source比NSTimer更精準(不受 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_t和pthread_mutex_t,提供wait、signal、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_setProperty和objc_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_state和dq_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)性資料。如有任何修改或補充需求,請隨時告知。