鎖是一種同步機制懦砂,用于多線程環(huán)境中對資源訪問的限制
iOS中常見鎖的性能對比圖(摘自:ibireme):
iOS鎖的介紹及使用
1.synchronized
代碼:
-(void)testSynchronized{
NSObject *obj = [NSObject new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(obj){
NSLog(@"線程一開始");
sleep(3);
NSLog(@"線程一結束");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(obj){
NSLog(@"線程二開始");
}
});
}
執(zhí)行結果:
2017-11-22 10:57:19.691200+0800 LiLLock[19850:2037312] 線程一開始
2017-11-22 10:57:22.695597+0800 LiLLock[19850:2037312] 線程一結束
2017-11-22 10:57:22.695803+0800 LiLLock[19850:2037311] 線程二開始
@synchronized(obj)指令使用的obj作為該鎖的唯一標識躺翻,只有當標識相同時稀拐,才能滿足互斥條件咬腋,如果線程二使用self作為標識,那么線程二就不會阻塞。@synchronized()指令的優(yōu)點是我們不需要顯示創(chuàng)建鎖對象,便可以方便實現(xiàn)鎖機制屋剑。但作為一種防護措施,@synchronized塊會隱式添加一個異常處理例程來保護代碼诗眨,該處理例程會在異常拋出的時自動釋放互斥鎖唉匾。如果不想隱式的異常處理例程消耗額外的資源,可以使用對象鎖匠楚。
@synchronized(obj){}內部obj被釋放或者被置為nil不會影響鎖的功能巍膘,如果obj一開始就被置為nil,那么就會丟失鎖的功能
2.NSLock
從NSLock類的.h文件中可以看出芋簿,NSLock峡懈、NSConditionLock、NSRecursiveLock与斤、NSCondition都遵循NSLocking 協(xié)議:
NSLock實現(xiàn)了最基礎的互斥鎖肪康,通過lock荚恶、unlock來加鎖和解鎖
代碼:
-(void)testNSLock{
NSLock *lock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"線程1加鎖成功");
sleep(3);
[lock unlock];
NSLog(@"線程1解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"線程2加鎖成功");
sleep(2);
[lock unlock];
NSLog(@"線程2解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
if ([lock tryLock]) {
NSLog(@"線程3加鎖成功");
[lock unlock];
NSLog(@"線程3解鎖成功");
}else{
NSLog(@"線程3加鎖失敗");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(6);
if ([lock tryLock]) {
NSLog(@"線程4加鎖成功");
[lock unlock];
NSLog(@"線程4解鎖成功");
}else{
NSLog(@"線程4加鎖失敗");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:7]]) {
NSLog(@"線程5加鎖成功");
[lock unlock];
NSLog(@"線程5解鎖成功");
}else{
NSLog(@"線程5加鎖失敗");
}
});
}
執(zhí)行結果:
2017-11-22 15:11:41.927360+0800 LiLLock[20818:2216630] 線程1加鎖成功
2017-11-22 15:11:42.929336+0800 LiLLock[20818:2216631] 線程3加鎖失敗
2017-11-22 15:11:44.930266+0800 LiLLock[20818:2216629] 線程2加鎖成功
2017-11-22 15:11:44.930284+0800 LiLLock[20818:2216630] 線程1解鎖成功
2017-11-22 15:11:46.934496+0800 LiLLock[20818:2216629] 線程2解鎖成功
2017-11-22 15:11:46.934548+0800 LiLLock[20818:2216632] 線程5加鎖成功
2017-11-22 15:11:46.934718+0800 LiLLock[20818:2216632] 線程5解鎖成功
2017-11-22 15:11:47.929784+0800 LiLLock[20818:2216633] 線程4加鎖成功
2017-11-22 15:11:47.929987+0800 LiLLock[20818:2216633] 線程4解鎖成功
綜上總結:
1.除了lock和unlock方法外,還提供了tryLock和lockBeforeDate:兩個方法
2.tryLock不會阻塞線程梅鹦,tryLock能加鎖返回YES裆甩,不能加鎖返回NO冗锁,都會執(zhí)行后續(xù)代碼
3.lockBeforeDate:會在指定時間之前嘗試加鎖齐唆,會阻塞線程,如果在指定時間之前能加鎖返回YES冻河,在指定時間之前都不能加鎖返回NO
4.由于是互斥鎖箍邮,當一個線程進行訪問的時候,該線程獲取到鎖叨叙,其他線程進行訪問的時候锭弊,被系統(tǒng)掛起,直到該線程釋放鎖擂错,其他線程才能對其進行訪問味滞,從而確保線程安全。如果連續(xù)鎖定兩次钮呀,則會造成死鎖問題剑鞍。
3.NSRecursiveLock
NSRecursiveLock遞歸鎖,可以被一個線程多次獲得而不會造成死鎖爽醋,它記錄了成功獲得鎖的次數蚁署,每一次成功獲得鎖必須有一個解鎖的操作與其對應,這樣才不會造成死鎖蚂四。NSRecursiveLock記錄了加鎖和解鎖的次數光戈,當二者平衡是,才能完全釋放鎖遂赠,被其他線程獲取久妆。
代碼:
-(void)testRecursiveLock{
NSRecursiveLock *lock = [NSRecursiveLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void(^block)(int index);
block = ^(int index){
[lock lock];
NSLog(@"%d加鎖成功",index);
if (index >0) {
block(index-1);
}
[lock unlock];
NSLog(@"%d解鎖成功",index);
};
block(2);
});
}
執(zhí)行結果:
2017-11-22 17:29:45.886354+0800 LiLLock[21169:2298294] 2加鎖成功
2017-11-22 17:29:45.886558+0800 LiLLock[21169:2298294] 1加鎖成功
2017-11-22 17:29:45.886654+0800 LiLLock[21169:2298294] 0加鎖成功
2017-11-22 17:29:45.886747+0800 LiLLock[21169:2298294] 0解鎖成功
2017-11-22 17:29:45.886850+0800 LiLLock[21169:2298294] 1解鎖成功
2017-11-22 17:29:45.887872+0800 LiLLock[21169:2298294] 2解鎖成功
綜上總結:
如果用NSLock來加鎖,lock先上了鎖跷睦,未解鎖就進入到遞歸的下一層筷弦,再次請求加鎖,線程就會被阻塞送讲,阻塞了線程后面的d代碼就不會被執(zhí)行奸笤,就不能解鎖,一直阻塞線程哼鬓,造成死鎖监右。在這種情況下,遞歸鎖就能很好的解決這些問題异希。
4.NSConditionLock
NSConditionLock對象所定義的互斥鎖可以在某些條件下進行加鎖和解鎖健盒,和NSLock類似,也遵循NSLocking協(xié)議,方法也類似扣癣,多了condition屬性惰帽,以及每個操作都多了一個關于condition屬性的方法,如:tryLock父虑、tryLockWhenCondition:该酗,所以NSConditionLock可以稱作條件鎖。
注意:只有當condition參數和初始化condition相同時士嚎,才能加鎖成功呜魄;unlockWithCondition:并不是滿足條件時才會解鎖,而是解鎖成功后修改condition的值莱衩。
代碼:
-(void)testNSConditionLock{
NSConditionLock *lock = [[NSConditionLock alloc]initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"線程1加鎖成功");
sleep(2);
[lock unlockWithCondition:3];
NSLog(@"線程1解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lockWhenCondition:2];
NSLog(@"線程2加鎖成功");
[lock unlockWithCondition:3];
NSLog(@"線程2解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([lock tryLockWhenCondition:2]) {
NSLog(@"線程3加鎖成功");
sleep(1);
[lock lockWhenCondition:3];
NSLog(@"線程3解鎖成功");
}else{
NSLog(@"線程3加鎖失敗");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([lock lockWhenCondition:3 beforeDate:[NSDate dateWithTimeIntervalSinceNow:5] ]) {
NSLog(@"線程4加鎖成功");
sleep(1);
[lock unlockWithCondition:2];
NSLog(@"線程4解鎖成功");
}else{
NSLog(@"線程4加鎖失敗");
}
});
}
執(zhí)行結果:
2017-11-22 18:37:26.109882+0800 LiLLock[21371:2343295] 線程3加鎖失敗
2017-11-22 18:37:26.109882+0800 LiLLock[21371:2343299] 線程1加鎖成功
2017-11-22 18:37:28.111479+0800 LiLLock[21371:2343299] 線程1解鎖成功
2017-11-22 18:37:28.111479+0800 LiLLock[21371:2343296] 線程4加鎖成功
2017-11-22 18:37:29.116467+0800 LiLLock[21371:2343296] 線程4解鎖成功
2017-11-22 18:37:29.116467+0800 LiLLock[21371:2343298] 線程2加鎖成功
2017-11-22 18:37:29.116664+0800 LiLLock[21371:2343298] 線程2解鎖成功
綜上總結:
部分相同方法見NSLock總結爵嗅,lockWhenCondition:當condition條件滿足時阻塞線程,不滿足條件時線性不進行任何操作笨蚁;tryLockWhenCondition:當condition條件滿足會嘗試加鎖睹晒,不會阻塞線程,成功返回YES括细,失敗返回NO伪很,都會執(zhí)行各自的后續(xù)操作;lockWhenCondition: beforeDate: 在指定的時間之前嘗試加鎖勒极,會阻塞線程是掰,如果在指定時間之前滿足condition條件加鎖成功返回YES,超過指定時間返回NO辱匿,執(zhí)行后續(xù)操作键痛。
5.NSCondition
NScondition是一種特殊類型的鎖,通過它可以實現(xiàn)不同線程的調度匾七。一個線程被某一個條件所阻塞絮短,直到另一個線程滿足該條件從而發(fā)信號給該線程使該線程可以正常運行。例如有一個下載圖片的線程昨忆,一個處理圖片的線程丁频,當沒有圖片處理時處理圖片的線程就會阻塞,當下載圖片的線程下載完圖片時滿足了處理圖片線程的需求邑贴,這個時候下載線程給處理線程一個信號席里,處理線程就會恢復運行。
注意:NSCondition對象實際上是一個鎖和一個線程檢查器拢驾,鎖上之后其他線程也可以上鎖奖磁,而之后根據條件決定是否繼續(xù)運行線程,即線程是否要進入waiting狀態(tài)繁疤。如果進入waiting狀態(tài)咖为,其他線程的該鎖執(zhí)行signal或者broadcast方法秕狰,線程被喚起,繼續(xù)運行躁染。NSCondition可以手動設置掛起和喚醒鸣哀,據此可以設置線程依賴。
代碼:
-(void)testNSCondition{
NSCondition *condition = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"線程1加鎖成功");
[condition wait];
NSLog(@"線程1被喚醒");
[condition unlock];
NSLog(@"線程1解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"線程2加鎖成功");
sleep(2);
if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"線程2被喚醒");
}
[condition unlock];
NSLog(@"線程2解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"線程3加鎖成功");
[condition wait];
NSLog(@"線程3被喚醒");
[condition unlock];
NSLog(@"線程3解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"線程4加鎖成功");
sleep(5);
[condition wait];
NSLog(@"線程4被喚醒后不解鎖");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"線程5加鎖成功");
sleep(7);
[condition wait];
NSLog(@"線程5被醒后不解鎖");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
[condition signal];
sleep(3);
[condition broadcast];
sleep(6);
[condition signal];
sleep(8);
[condition signal];
});
}
執(zhí)行結果:
2017-11-23 11:13:47.710664+0800 LiLLock[22731:2495287] 線程1加鎖成功
2017-11-23 11:13:47.711805+0800 LiLLock[22731:2495289] 線程2加鎖成功
2017-11-23 11:13:49.715733+0800 LiLLock[22731:2495290] 線程3加鎖成功
2017-11-23 11:13:49.716027+0800 LiLLock[22731:2495314] 線程4加鎖成功
2017-11-23 11:13:54.719161+0800 LiLLock[22731:2495430] 線程5加鎖成功
2017-11-23 11:14:01.722348+0800 LiLLock[22731:2495287] 線程1被喚醒
2017-11-23 11:14:01.722513+0800 LiLLock[22731:2495287] 線程1解鎖成功
2017-11-23 11:14:01.722535+0800 LiLLock[22731:2495290] 線程3被喚醒
2017-11-23 11:14:01.722677+0800 LiLLock[22731:2495290] 線程3解鎖成功
2017-11-23 11:14:01.722697+0800 LiLLock[22731:2495289] 線程2被喚醒
2017-11-23 11:14:01.722774+0800 LiLLock[22731:2495289] 線程2解鎖成功
2017-11-23 11:14:01.722783+0800 LiLLock[22731:2495314] 線程4被喚醒后不解鎖
綜上總結:
1.加上鎖之后吞彤,調用條件對象的wait或者waitUnitilDate:來阻塞線程我衬,直到條件對象發(fā)出信號或者超時才會執(zhí)行后續(xù)操作。
2.signal和broadcast的區(qū)別是:signal是一個信號量备畦,只能喚起一個等待的線程低飒,要想喚起多個線程必須多次調用,而broadcast可以喚起所有等待的線程懂盐。
3.signal或者broadcast要想喚起線程必須要有喚醒對象解鎖,否則無法喚起糕档,線程被喚起后必須有解鎖操作莉恼,否則之后無法喚起其他線程,其他線程也無法加鎖速那。
6.dispatch_semaphore
dispatch_semaphore使用信號量機制實現(xiàn)鎖俐银,等待信號和發(fā)送信號。
dispatch_semaphore是GCD用于同步的一種方式端仰,與他相關的方法有三個捶惜,一個是創(chuàng)建信號,一個是等待信號荔烧,一個是發(fā)送信號吱七。
dispatch_semaphore的機制就是當有多個線程進行訪問的時候,只要有一個獲得信號鹤竭,其他的線程就必須等待該信號釋放踊餐。
代碼:
-(void)testDispatch_semaphore{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_time_t overtime = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC6);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, overtime);
NSLog(@"線程1開始");
sleep(3);
NSLog(@"線程1結束");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, overtime);
NSLog(@"線程2開始");
sleep(5);
NSLog(@"線程2結束");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, overtime);
NSLog(@"線程3開始");
sleep(1);
NSLog(@"線程3結束");
dispatch_semaphore_signal(semaphore);
});
}
執(zhí)行結果:
-(void)testDispatch_semaphore{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_time_t overtime = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC4);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, overtime);
NSLog(@"線程1開始");
sleep(3);
NSLog(@"線程1結束");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(semaphore, overtime);
NSLog(@"線程2開始");
sleep(5);
NSLog(@"線程2結束");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
dispatch_semaphore_wait(semaphore, overtime);
NSLog(@"線程3開始");
NSLog(@"線程3結束");
dispatch_semaphore_signal(semaphore);
});
}
綜上總結:
1.dispatch_semaphore和condition類似,都是一種基于信號的同步方式臀稚,但是NSCondition只能發(fā)送吝岭,不能保存(如果沒有線程在等待,則發(fā)送的信號失效)吧寺。而dispatch_semaphore能保存發(fā)送的信號窜管。dispatch_semaphore的核心是dispatch——semaphore_t類型的信號量;
2.dispatch_semaphore_create(1)方法可以創(chuàng)建一個dispatch_semaphore_t類型的信號量稚机,設定信號量的初始值為1幕帆。注意這里的傳入參數必須大于或者等于0,否則dispatch_semaphore_create會返回NULL抒钱。
3.dispatch_semaphore_wait(semaphore,overTime)蜓肆;方法會判斷semaphore的信號值是否大于0.大于0不會阻塞線程颜凯,消耗掉一個信號,執(zhí)行后續(xù)任務仗扬。如果信號量為0症概,該線程會和NScondition一樣直接進入waiting狀態(tài),等待其他線程發(fā)送信號喚醒線程去執(zhí)行后續(xù)任務早芭,或者當overTime時限到了彼城,也會執(zhí)行后續(xù)任務。
4.dispatch_semaphore_signal(semaphore)退个;發(fā)送信號募壕,如果沒有等待的線程接受信號,則使signal信號值加一(做到對信號的保存)语盈。
5.一個dispatch_semaphore_wait(semaphore,overTime);方法會去對應一個dispatch_semaphore_singal(semaphore);看起來像NSLock的lock和unlock舱馅,其實可以這樣理解,區(qū)別只在于信號量這個參數刀荒,lock unlcok只能同意時間代嗤,一個線程訪問被保護的臨界區(qū),而如果dispatch_semaphore的信號量的初始值為x缠借,則可以有x個線程同時訪問被保護的臨界區(qū)干毅。
7.pthread_mutex和pthread_mutex(recursive)
pthread標識POSIX thread,定義了一組跨平臺的線程相關的API泼返,POSIX互斥鎖是一種超級易用的互斥鎖硝逢,使用的時候:
1.只需要使用pthread_mutex_init癡實話一個pthread_mutex_t,
2.pthread_mutex_lock或者pthread_mutex_trylock來鎖定绅喉,
3pthread_mutex_unlcok來解鎖渠鸽,
4.當使用完成后,記得調用pthread_mutex_destory來銷毀霹疫。
代碼:
-(void)testPthread_mutex{
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
pthread_mutex_lock(&mutex);
NSLog(@"線程1開始");
sleep(3);
pthread_mutex_unlock(&mutex);
NSLog(@"線程1結束");
});
dispatch_async(concurrentQueue, ^{
sleep(1);
pthread_mutex_lock(&mutex);
NSLog(@"線程2開始");
pthread_mutex_unlock(&mutex);
NSLog(@"線程2結束");
});
__block pthread_mutex_t mutex1;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex1, &attr);
pthread_mutexattr_destroy(&attr);
dispatch_barrier_async(concurrentQueue, ^{
static void(^block)(int index);
block = ^(int index){
pthread_mutex_lock(&mutex1);
NSLog(@"線程3遞歸%d開始",index);
if (index > 0) {
block(index-1);
}
pthread_mutex_unlock(&mutex1);
NSLog(@"線程3遞歸%d結束",index);
};
block(3);
});
}
執(zhí)行結果:
2017-11-23 15:01:00.738222+0800 LiLLock[23523:2663942] 線程1開始
2017-11-23 15:01:03.741337+0800 LiLLock[23523:2663941] 線程2開始
2017-11-23 15:01:03.741337+0800 LiLLock[23523:2663942] 線程1結束
2017-11-23 15:01:03.741527+0800 LiLLock[23523:2663941] 線程2結束
2017-11-23 15:01:03.741647+0800 LiLLock[23523:2663941] 線程3遞歸3開始
2017-11-23 15:01:03.741744+0800 LiLLock[23523:2663941] 線程3遞歸2開始
2017-11-23 15:01:03.741842+0800 LiLLock[23523:2663941] 線程3遞歸1開始
2017-11-23 15:01:03.742096+0800 LiLLock[23523:2663941] 線程3遞歸0開始
2017-11-23 15:01:03.742195+0800 LiLLock[23523:2663941] 線程3遞歸0結束
2017-11-23 15:01:03.742291+0800 LiLLock[23523:2663941] 線程3遞歸1結束
2017-11-23 15:01:03.742528+0800 LiLLock[23523:2663941] 線程3遞歸2結束
2017-11-23 15:01:03.742627+0800 LiLLock[23523:2663941] 線程3遞歸3結束
綜上總結:
1.它的用法和NSLock芙蓉lock unlcok用法一致拱绑,而它也有一個pthread_mutex_trylock方法,pthread_mutex_trylock和tryLock芙蓉區(qū)別在于丽蝎,tryLcok返回的是YES和NO猎拨,pthread_mutex_trylock加鎖成功返回的是0,失敗返回的是錯誤提示碼屠阻。
2.pthread_mutex(&recursive)作用和NSRecursiveLock遞歸鎖類似红省,如果使用pthread_mutex_init(&theLock,NULL);初始化鎖的畫,上面的代碼的第二部分就會出現(xiàn)死鎖現(xiàn)象国觉,使用遞歸鎖就可以避免這種現(xiàn)象吧恃。
8.OSSpinLock
OSSpinLock是一種自旋鎖,和互斥鎖類似麻诀,都是為了保證線程安全的鎖痕寓。大門二者的區(qū)別是不一樣的傲醉,對于互斥鎖,當一個線程獲得這個鎖之后呻率,其他想要獲得所的線程將會被阻塞硬毕,直到該鎖被釋放。但是自旋鎖不一樣礼仗,當一個線程獲得鎖之后吐咳,其他線程將會一直循環(huán)在哪里查看該鎖是否被釋放,所以此鎖比較適用于鎖的持有者保存時間較短的情況下元践。
代碼:
-(void)testOSSpinLock{
__block OSSpinLock theLock = OS_SPINLOCK_INIT;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
NSLog(@"線程1開始");
sleep(3);
OSSpinLockUnlock(&theLock);
NSLog(@"線程1結束");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
OSSpinLockLock(&theLock);
NSLog(@"線程2開始");
OSSpinLockUnlock(&theLock);
NSLog(@"線程2結束");
});
}
執(zhí)行結果:
2017-11-23 15:25:40.096050+0800 LiLLock[23666:2693366] 線程1開始
2017-11-23 15:25:43.096610+0800 LiLLock[23666:2693366] 線程1結束
2017-11-23 15:25:43.153535+0800 LiLLock[23666:2693364] 線程2開始
2017-11-23 15:25:43.154364+0800 LiLLock[23666:2693364] 線程2結束
注:YY@ibireme的文章闡述了自旋鎖存在優(yōu)先級反轉問題韭脊,具體文章可以點擊此處而OSSpinLock在iOS10.0中被<os/lock.h>中的os_unfair_lock取代了。
9.os_unfair_lock
這個鎖就是OSSpinLock的替代单旁,用來解決優(yōu)先級反轉問題沪羔。
代碼:
-(void)testOs_unfair_lock{
__block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
os_unfair_lock_lock(&unfairLock);
NSLog(@"線程1開始");
sleep(3);
os_unfair_lock_unlock(&unfairLock);
NSLog(@"線程1結束");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
sleep(1);
os_unfair_lock_lock(&unfairLock);
NSLog(@"線程2開始");
os_unfair_lock_unlock(&unfairLock);
NSLog(@"線程2結束");
});
}
執(zhí)行結果:
2017-11-23 15:46:16.346936+0800 LiLLock[23897:2720025] 線程1開始
2017-11-23 15:46:19.418207+0800 LiLLock[23897:2720021] 線程2開始
2017-11-23 15:46:19.418245+0800 LiLLock[23897:2720025] 線程1結束
2017-11-23 15:46:19.418392+0800 LiLLock[23897:2720021] 線程2結束
總結:
1.每一種鎖的執(zhí)行步驟基本都是加鎖、等待慎恒、解鎖任内。
2.@synchronized()執(zhí)行效率最低,但是使用起來方便融柬,如果沒有什么性能瓶頸可以使用。
3.OSSpinLock的效率最高趋距,但是存在優(yōu)先級反轉問題不安全粒氧,iOS 10之后出了os_unfair_lock來代替OSSpinLock,dispatch_semaphore和pthread_mutex的執(zhí)行效率緊隨其后节腐,既可以保證效率又可以保證安全是比較好的選擇外盯。
4.遵循NSLocking的鎖,執(zhí)行效率為NSLock>NSCondition>NSRecursiveLock>NSConditionLock