前言
多線程是現(xiàn)在每個(gè)開發(fā)必定知道的。這哥們(多線程)是用來干啥的?舉個(gè)例子拿單線程來說,單線程就是你寫的代碼一步一步執(zhí)行,完全按照順序執(zhí)行,科技一步一步進(jìn)步,現(xiàn)在CPU核心數(shù)越來越多,多線程也就成為了現(xiàn)在每個(gè)程序員必回的一個(gè)知識(shí)點(diǎn)。但是使用多線程優(yōu)點(diǎn)就不多說了。要說都是優(yōu)點(diǎn)沒缺點(diǎn),這純瞎說,他的弊端就是資源搶占問題,開辟多條線程占用一定的資源(主線程一般1MB ,其他線程512kb,一般建議同時(shí)最多開三條線程比較合理)。關(guān)于資源搶占問題下面就舉個(gè)例子:在使用多線程的時(shí)候多個(gè)線程可能會(huì)訪問同一塊資源,這樣就很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全等問題。解決資源爭(zhēng)用,最直接的想法是引入鎖,對(duì)并發(fā)讀寫的數(shù)據(jù)進(jìn)行保護(hù),保證每次只有一個(gè)線程訪問這一塊資源。
??鎖是最常用的同步工具:一塊公共資源在同一個(gè)時(shí)間只能允許被一個(gè)線程訪問,比如一個(gè)線程A進(jìn)入加鎖資源之后,由于已經(jīng)加鎖,另一個(gè)線程B就無法訪問,只有等待前一個(gè)線程A執(zhí)行完后解鎖,B線程才能訪問加鎖資源。
為什么需要鎖?
?以常見的火車站賣票為例,假設(shè)有20張票,有兩個(gè)窗口同時(shí)售票:
- (void)ticketTest{
self.ticketsCount = 20;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 線程1
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];//多線程售票
}
});
// 線程2
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];//多線程售票
}
});
}
- (void)sellingTickets{
NSInteger oldMoney = self.ticketsCount;
sleep(0.2);
oldMoney -= 1;
self.ticketsCount = oldMoney;
NSLog(@"當(dāng)前剩余票數(shù)-> %ld", oldMoney);
}
不加鎖
2019-02-21 17:10:40.861358+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 19
2019-02-21 17:10:40.861358+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 19
2019-02-21 17:10:40.861723+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 18
2019-02-21 17:10:40.861723+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 18
2019-02-21 17:10:40.861851+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 17
2019-02-21 17:10:40.861961+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 16
2019-02-21 17:10:40.861989+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 16
2019-02-21 17:10:40.862066+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 15
2019-02-21 17:10:40.862222+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 14
2019-02-21 17:10:40.863234+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 13
2019-02-21 17:10:40.863958+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 12
2019-02-21 17:10:40.864225+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 11
2019-02-21 17:10:40.864529+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 10
2019-02-21 17:10:40.865159+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 9
2019-02-21 17:10:40.865498+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 8
2019-02-21 17:10:40.865777+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 7
2019-02-21 17:10:40.866747+0800 多線程測(cè)試[8167:20874670] 當(dāng)前剩余票數(shù)-> 6
2019-02-21 17:10:40.866970+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 5
2019-02-21 17:10:40.867402+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 4
2019-02-21 17:10:40.867879+0800 多線程測(cè)試[8167:20874671] 當(dāng)前剩余票數(shù)-> 3
不加鎖時(shí)很明顯數(shù)據(jù)發(fā)生了混亂。
iOS中都有哪些鎖?
敲黑板,講重點(diǎn)。
從大的方向講有兩種鎖:
互斥鎖
自旋鎖。
這兩種類型下分別有自己對(duì)應(yīng)的鎖:
互斥鎖和自旋鎖的對(duì)比:
這兩種鎖的相同點(diǎn)不必多說,都可以避免多線程訪問同一個(gè)值發(fā)生混亂,重點(diǎn)說一下兩種的不同點(diǎn):
互斥鎖:如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了,線程會(huì)進(jìn)入休眠狀態(tài)等待鎖。一旦被訪問的資源被解鎖, 則等待資源的線程會(huì)被喚醒
自旋鎖:如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了,線程會(huì)以死循環(huán)的方式等待鎖,一旦被訪問的資源被解鎖, 則等待資源的線程會(huì)立即執(zhí)行
自旋鎖的特點(diǎn):
自旋鎖的性能高于互斥鎖,因?yàn)轫憫?yīng)速度快
自旋鎖雖然會(huì)一直自旋等待獲取鎖,但不會(huì)一直占用CPU,超過了操作系統(tǒng)分配的時(shí)間片會(huì)被強(qiáng)制掛起
自旋鎖如果不能保證所有線程都是同一優(yōu)先級(jí),則可能造成死鎖。
因?yàn)橐陨系奶攸c(diǎn),自旋鎖和互斥鎖也有不同的使用場(chǎng)景:
多核處理器情況下: 如果預(yù)計(jì)線程等待鎖的時(shí)間比較短,短到比線程兩次切換上下文的時(shí)間還要少的情況下,自旋鎖是更好的選擇。
如果時(shí)間比較長,則互斥鎖是比較好的選擇。 單核處理器情況下: 不建議使用自旋鎖。
從詳細(xì)來分鎖:
@synchronized
NSLock 對(duì)象鎖
NSRecursiveLock遞歸鎖
NSConditionLock 條件鎖
pthread_mutex 互斥鎖(C語言)
dispatch_semaphore 信號(hào)量實(shí)現(xiàn)加鎖(GCD)
OSSpinLock 自旋鎖
具體使用請(qǐng)移步google。