前言
最開始我想把線程和線程鎖放在一起整理出一篇文章,結(jié)果整理了線程發(fā)現(xiàn)有點長,于是便把線程鎖單獨拿出來了。感興趣的小伙伴也可以去看下線程的生命周期,NSThread、GCD、NSOperation的使用與總結(jié),因為兩篇文章原本是想放在一起的。
正文
一、鎖的一些概念和性能對比
1.1 為什么要使用鎖(線程安全)
線程安全是指,當(dāng)一個線程訪問數(shù)據(jù)的時候,其他的線程不能對其進行訪問,直到該線程訪問完畢。簡單來講就是在同一時刻,對同一個數(shù)據(jù)操作的線程只有一個。只有確保了這樣,才能使數(shù)據(jù)不會被其他線程影響。而線程不安全,則是在同一時刻可以有多個線程對該數(shù)據(jù)進行訪問,從而得不到預(yù)期的結(jié)果。
舉例來說:現(xiàn)在僅剩余一張火車票,每一個購票請求都是一個線程,那么同一時刻有多個線程同時請求出票,那么剩余的這一張票將會同時出票多次,這明顯是不合理的,所以鎖的出現(xiàn),就是為了確保線程安全問題。
1.2 鎖的一些概念
臨界資源: 多個線程共享各種資源,然而有很多資源一次只能供一個線程使用。一次僅允許一個線程使用的資源稱為臨界資源。
臨界區(qū):訪問臨界資源的代碼區(qū)。
死鎖:指兩個或兩個以上的線程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象(都要等待對方完成某個操作才能進行下一步),若無外力作用,它們都將無法推進下去,這時就會發(fā)生死鎖。
上下文切換(Context Switch):在操作系統(tǒng)中,CPU切換到另一個進程需要保存當(dāng)前進程的狀態(tài)并恢復(fù)另一個進程的狀態(tài):當(dāng)前運行任務(wù)轉(zhuǎn)為就緒(或者掛起、刪除)狀態(tài),另一個被選定的就緒任務(wù)成為當(dāng)前任務(wù)。上下文切換包括保存當(dāng)前任務(wù)的運行環(huán)境,恢復(fù)將要運行任務(wù)的運行環(huán)境。
輪詢(Polling):一種CPU決策如何提供周邊設(shè)備服務(wù)的方式,又稱“程控輸入輸出”(Programmed I/O)。輪詢法的概念是,由CPU定時發(fā)出詢問,依序詢問每一個周邊設(shè)備是否需要其服務(wù),有即給予服務(wù),服務(wù)結(jié)束后再問下一個周邊,接著不斷周而復(fù)始。
-
原子屬性:
atomic:原子屬性,設(shè)置成員變量的@property屬性時,默認(rèn)為atomic,提供多線程安全。atomic是為setter方法加鎖,將屬性以atomic的形式來聲明,該屬性變量就能支持互斥鎖了。而這種機制是耗費系統(tǒng)資源的。
nonatomic:非原子屬性,不會為setter方法加鎖,聲明為該屬性的變量,客戶端應(yīng)盡量避免多線程爭奪同一資源。
自旋鎖:線程反復(fù)檢查鎖變量是否可用(類似于
while(鎖沒解開)),因此是一種忙等狀態(tài),將一直占用CPU資源。讀寫鎖:讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對于自旋鎖而言,能提高并發(fā)性,因為在多處理器系統(tǒng)中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數(shù)為實際的邏輯CPU數(shù)。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數(shù)相關(guān)),但不能同時既有讀者又有寫者。
互斥鎖:在被訪問的資源被鎖時,會讓當(dāng)前線程進入休眠狀態(tài),不再占用CPU資源,一旦被訪問的資源被解鎖,則等待資源的線程會被喚醒。
條件鎖:在一定條件下,讓其等待休眠,并放開鎖,等接收到信號或者廣播,會從新喚起線程,并重新加鎖。
遞歸鎖:同一個線程可以多次加鎖,不會造成死鎖,不同線程來訪問這段代碼時,發(fā)現(xiàn)有鎖要等待所有鎖解開之后才可以繼續(xù)往下走。
信號量(Semaphore):有時被稱為信號燈,是在多線程環(huán)境下使用的一種設(shè)施,是可以用來保證兩個或多個關(guān)鍵代碼段不被并發(fā)調(diào)用。在進入一個關(guān)鍵代碼段之前,線程必須獲取一個信號量;一旦該關(guān)鍵代碼段完成了,那么該線程必須釋放信號量。其它想進入該關(guān)鍵代碼段的線程必須等待直到第一個線程釋放信號量。
1.3 性能對比

//10000000
OSSpinLock: 112.38 ms
dispatch_semaphore: 160.37 ms
os_unfair_lock: 208.87 ms
pthread_mutex: 302.07 ms
NSCondition: 320.11 ms
NSLock: 331.80 ms
pthread_rwlock: 360.81 ms
pthread_mutex(recursive): 512.17 ms
NSRecursiveLock: 667.55 ms
NSConditionLock: 999.91 ms
@synchronized: 1654.92 ms
//1000
OSSpinLock: 0.02 ms
dispatch_semaphore: 0.03 ms
os_unfair_lock: 0.04 ms
pthread_mutex: 0.06 ms
NSLock: 0.06 ms
pthread_rwlock: 0.07 ms
NSCondition: 0.07 ms
pthread_mutex(recursive): 0.09 ms
NSRecursiveLock: 0.12 ms
NSConditionLock: 0.18 ms
@synchronized: 0.33 ms
二、鎖的使用(種類)
上鎖有兩種方式trylock和lock:當(dāng)前線程鎖失敗,也可以繼續(xù)其它任務(wù),用 trylock 合適;當(dāng)前線程只有鎖成功后,才會做一些有意義的工作,那就 lock,沒必要輪詢 trylock。
注:以下大部分鎖都會提供trylock接口,不再作解釋。
2.1 OSSpinLock (自旋鎖)
- OSSpinLock 是一種自旋鎖,也只有加鎖,解鎖,嘗試加鎖三個方法。
- OSSpinLock 會一直輪詢,等待時會消耗大量 CPU 資源,不適用于較長時間的任務(wù)。
- OSSpinLock 有潛在的優(yōu)先級反轉(zhuǎn)問題,iOS10.0以后棄用了這種鎖機制,使用os_unfair_lock。
需要導(dǎo)入頭文件
#import <libkern/OSAtomic.h>
// 初始化
OSSpinLock spinLock = OS_SPINLOCK_INIT;
// 加鎖
OSSpinLockLock(&spinLock);
// 解鎖
OSSpinLockUnlock(&spinLock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
OSSpinLockTry(&spinLock)
以GCD為例,代碼以及執(zhí)行結(jié)果如下:
2.1.1 OSSpinLockLock
// OSSpinLockLock
- (void)UseOSSpinLock{
__block OSSpinLock spinLock = OS_SPINLOCK_INIT;
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
OSSpinLockLock(&spinLock);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程1 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
});
dispatch_async(queue, ^{
OSSpinLockLock(&spinLock);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程2 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
});
dispatch_async(queue, ^{
OSSpinLockLock(&spinLock);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程3 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
});
}

我們可以看到,我們用的并發(fā)異步線程,但是加鎖之后,執(zhí)行結(jié)果并沒有并發(fā)異步執(zhí)行。
2.1.2 OSSpinLockTry
// OSSpinLockTry
- (void)UseOSSpinLock{
__block OSSpinLock spinLock = OS_SPINLOCK_INIT;
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
OSSpinLockTry(&spinLock);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程4 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
});
dispatch_async(queue, ^{
OSSpinLockTry(&spinLock);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程5 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
});
dispatch_async(queue, ^{
OSSpinLockTry(&spinLock);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程6 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
});
}

執(zhí)行結(jié)果可以看出來,OSSpinLockTry并沒有阻塞線程。也符合上面所說:當(dāng)前線程鎖失敗,也可以繼續(xù)其它任務(wù)。但是這只是測試一下,項目中不要這么寫,因為這樣沒有意義,可以如下:
//OSSpinLockTry
- (void)UseOSSpinLock{
__block OSSpinLock spinLock = OS_SPINLOCK_INIT;
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
if(OSSpinLockTry(&spinLock)){
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程7 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
}else{
NSLog(@"鎖7失敗,執(zhí)行一些其他事情");
}
});
dispatch_async(queue, ^{
if(OSSpinLockTry(&spinLock)){
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程8 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
}else{
NSLog(@"鎖8失敗,執(zhí)行一些其他事情");
}
});
dispatch_async(queue, ^{
if(OSSpinLockTry(&spinLock)){
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程9 第 %d 次",i);
}
OSSpinLockUnlock(&spinLock);
}else{
NSLog(@"鎖9失敗,執(zhí)行一些其他事情");
}
});
}

執(zhí)行結(jié)果可以看出,加鎖失敗后執(zhí)行另一部分代碼,并沒有自旋去等待加鎖,執(zhí)行后其他鎖釋放也不會再次加鎖,所以用的時候要考慮場景。
2.2 os_unfair_lock(互斥鎖)
os_unfair_lock網(wǎng)上很多文章,有說它是自旋鎖的,但是官方文檔中有一段是這樣的:
This is a replacement for the deprecated OSSpinLock. This function doesn't spin on contention, but instead waits in the kernel to be awoken by an unlock.
自我理解為:“這是對已棄用的osspinlock的替換。這個函數(shù)不會在爭用時自旋,而是在內(nèi)核中等待解鎖來喚醒?!彼?,它應(yīng)該是互斥鎖,并不是自旋鎖。
需要導(dǎo)入頭文件
#import <os/lock.h>
// 初始化
os_unfair_lock unfair_lock = OS_UNFAIR_LOCK_INIT;
// 加鎖
os_unfair_lock_lock(&unfair_lock);
// 解鎖
os_unfair_lock_unlock(&unfair_lock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
os_unfair_lock_trylock(&unfair_lock);
/*
注:解決不同優(yōu)先級的線程申請鎖的時候不會發(fā)生優(yōu)先級反轉(zhuǎn)問題.
不過相對于 OSSpinLock , os_unfair_lock性能方面減弱了許多.
*/
使用方法上同,不做示范。
2.3 dispatch_semaphore (信號量)
信號量,是持有計數(shù)的信號。個人覺得有點類似于引用計數(shù):create時定義最大線程數(shù),使用時wait進行計數(shù)-1,結(jié)束時signal進行計數(shù)+1,當(dāng)計數(shù)大于零時可執(zhí)行,等于零時阻塞線程進行等待執(zhí)行。
信號量的作用,個人覺得有以下幾點:
- 控制線程數(shù)量(最大并發(fā)數(shù)),我們知道
NSOperationQueue我們可以設(shè)置maxConcurrentOperationCount來控制最大并發(fā)數(shù),但是GCD的話,并沒有一個屬性可以控制最大并發(fā)數(shù),所以我們可以用信號量來控制GCD的最大并發(fā)數(shù)。 - 線程安全(鎖),信號量的本質(zhì)還是控制子線程并發(fā)數(shù)量,而我們可以設(shè)置最大并發(fā)量為1,然后在臨界區(qū)(多條線程都會訪問的代碼區(qū))進行信號量控制,保證同一時刻只有一條線程執(zhí)行此段代碼,從而保證線程安全。
- 線程同步,會在后面代碼說明。
- 阻塞線程,會在后面和同步代碼一起說明。
// 初始化
dispatch_semaphore_t semaphore_t = dispatch_semaphore_create(1);
// 加鎖
dispatch_semaphore_wait(semaphore_t,DISPATCH_TIME_FOREVER);
// 解鎖
dispatch_semaphore_signal(semaphore_t);
2.3.1 最大并發(fā)數(shù)
不多說,直接上代碼:
//2.dispatch_semaphore
- (void)UseSemaphore{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程1 第%d次 線程:%@",i,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程2 第%d次 線程:%@",i,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程3 第%d次 線程:%@",i,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for(int i = 0; i < 3; i++){
sleep(1);
NSLog(@"線程4 第%d次 線程:%@",i,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
});
}

通過結(jié)果我們可以看到,一共是四個并發(fā)異步線程,但是由于設(shè)置信號量,間接控制了最大并發(fā)數(shù)。值得注意的是:最大并發(fā)數(shù),是指執(zhí)行任務(wù)的線程最多是兩個(信號量設(shè)置的是兩個),但是,處于回收狀態(tài)的線程不算此列.也就是說,執(zhí)行任務(wù)的時候不只有兩個線程,還有處于回收狀態(tài)的線程,所以子線程個數(shù)不為2;
2.3.2 線程安全
線程不安全時:
- (void)UseSemaphoreLock{
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
self.number = 50;
__weak typeof(self) weakSelf = self;
dispatch_async(queue, ^{
for(int i = 0; i < 3; i++){
[weakSelf changeNumber];
}
});
dispatch_async(queue, ^{
for(int i = 0; i < 3; i++){
[weakSelf changeNumber];
}
});
}
- (void)changeNumber{
_number = _number - 1;
sleep(1);
NSLog(@"number == %ld",_number);
}

我們可以看到,如果異步線程,同時更改同意資源時,那么可能出現(xiàn)數(shù)據(jù)混亂。所以我們可以用信號量加鎖,保證線程安全:
- (void)UseSemaphoreLock{
self.semaphore1 = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
self.number = 50;
__weak typeof(self) weakSelf = self;
dispatch_async(queue, ^{
for(int i = 0; i < 3; i++){
[weakSelf changeNumber];
}
});
dispatch_async(queue, ^{
for(int i = 0; i < 3; i++){
[weakSelf changeNumber];
}
});
}
- (void)changeNumber{
//相當(dāng)于加鎖
dispatch_semaphore_wait(_semaphore1, DISPATCH_TIME_FOREVER);
_number = _number - 1;
sleep(1);
NSLog(@"number == %ld",_number);
//相當(dāng)于解鎖
dispatch_semaphore_signal(_semaphore1);
}

如上,用信號量可以處理線程安全問題。(當(dāng)然我們也可以把
wait、signal加到異步線程當(dāng)中,但是覺得那么做的話,實際上還是控制了最大并發(fā)數(shù),并不是解決線程安全。)
2.3.3 線程同步
//semaphore實現(xiàn)線程同步
- (void)semaphoreSync {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int number = 0;
dispatch_async(queue, ^{
// 追加任務(wù)1
[NSThread sleepForTimeInterval:1]; // 模擬耗時操作
NSLog(@"任務(wù)1 %@",[NSThread currentThread]); // 打印當(dāng)前線程
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end1,number = %d",number);
dispatch_async(queue, ^{
// 追加任務(wù)2
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"任務(wù)2 %@",[NSThread currentThread]); // 打印當(dāng)前線程
number = 50;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end2,number = %d",number);
dispatch_async(queue, ^{
// 追加任務(wù)3
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"任務(wù)3 %@",[NSThread currentThread]); // 打印當(dāng)前線程
number = 10;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end3,number = %d",number);
}

可以看到,雖然我們建立的并行異步線程,但是執(zhí)行結(jié)果卻是同步執(zhí)行。原因如下:在主線程程中設(shè)置信號量為0
dispatch_semaphore_create(0);當(dāng)執(zhí)行到追加任務(wù)1的子線程時,進入子線程,主線程繼續(xù)執(zhí)行dispatch_semaphore_wait,但是此時信號量為0,dispatch_semaphore_wait在此處阻塞主線程進入等待狀態(tài),直到任務(wù)1的子線程執(zhí)行dispatch_semaphore_signal使信號量+1,此時主線程中處于等待的dispatch_semaphore_wait可以使信號量-1,于是停止阻塞線程并繼續(xù)向下執(zhí)行。(利用阻塞線程實現(xiàn)線程同步)
2.4 pthread_mutex(互斥鎖)
-
pthread_mutex是 C 語言下多線程加互斥鎖的方式。 - 被這個鎖保護的臨界區(qū)就只允許一個線程進入,其它線程如果沒有獲得鎖權(quán)限,那就只能在外面等著。
需要導(dǎo)入頭文件
#import <pthread/pthread.h>
//聲明鎖
pthread_mutex_t mutex_t;
// 初始化(兩種)
1.普通初始化
pthread_mutex_init(&mutex_t, NULL);
2.宏初始化
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
// 加鎖
pthread_mutex_lock(&mutex_t);
// 解鎖
pthread_mutex_unlock(&mutex_t);
// 嘗試加鎖,可以加鎖時返回的是 0,否則返回一個錯誤
pthread_mutex_trylock(& mutex_t)
// 釋放鎖
pthread_mutex_destroy(&_lock)
- 鎖類型:
pthread_mutex_init(&mutex_t, NULL);
初始化鎖 NULL等同于PTHREAD_MUTEX_DEFAULT
PTHREAD_MUTEX_NORMAL 缺省類型,也就是普通鎖。當(dāng)一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,并在解鎖后先進先出原則獲得鎖。
PTHREAD_MUTEX_ERRORCHECK 檢錯鎖,如果同一個線程請求同一個鎖,則返回 EDEADLK,否則與普通鎖類型動作相同。這樣就保證當(dāng)不允許多次加鎖時不會出現(xiàn)嵌套情況下的死鎖。
PTHREAD_MUTEX_RECURSIVE 遞歸鎖,允許同一個線程對同一個鎖成功獲得多次,并通過多次 unlock 解鎖。
PTHREAD_MUTEX_DEFAULT 適應(yīng)鎖,動作最簡單的鎖類型,僅等待解鎖后重新競爭,沒有等待隊列
2.5 NSCondition(條件鎖、對象鎖)
// 初始化
NSCondition *_condition= [[NSCondition alloc]init];
// 加鎖
[_condition lock];
// 解鎖
[_condition unlock];
/*
其他功能接口
wait 進入等待狀態(tài)
waitUntilDate:讓一個線程等待一定的時間
signal 喚醒一個等待的線程
broadcast 喚醒所有等待的線程
注: 所測時間波動太大, 有時候會快于 NSLock, 我取得中間值.
*/
2.6 NSLock(互斥鎖、對象鎖)
// 初始化
NSLock *_lock = [[NSLock alloc]init];
// 加鎖
[_lock lock];
// 解鎖
[_lock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_lock tryLock];
2.7 pthread_rwlock(讀寫鎖)
讀寫鎖又稱共享-互斥鎖:
//加讀鎖
pthread_rwlock_rdlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
//加寫鎖
pthread_rwlock_wrlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
2.8 pthread_mutex(recursive)(遞歸鎖)
// 初始化
pthread_mutex_t mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr并且給它賦予默認(rèn)pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //設(shè)置鎖類型,這邊是設(shè)置為遞歸鎖
pthread_mutex_init(&mutex_t, &attr);
pthread_mutexattr_destroy(&attr); //銷毀一個屬性對象,在重新進行初始化之前該結(jié)構(gòu)不能重新使用
// 加鎖
pthread_mutex_lock(&mutex_t);
// 解鎖
pthread_mutex_unlock(&mutex_t);
/*
注: 遞歸鎖可以被同一線程多次請求,而不會引起死鎖。
即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。
這主要是用在循環(huán)或遞歸操作中。
*/
2.9 NSRecursiveLock(遞歸鎖、對象鎖)
// 初始化
NSRecursiveLock *_recursiveLock = [[NSRecursiveLock alloc]init];
// 加鎖
[_recursiveLock lock];
// 解鎖
[_recursiveLock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_recursiveLock tryLock];
/*
注: 遞歸鎖可以被同一線程多次請求,而不會引起死鎖。
即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。
這主要是用在循環(huán)或遞歸操作中。
- (BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時間之內(nèi)
*/
2.10 NSConditionLock(條件鎖、對象鎖)
// 初始化
NSConditionLock *_conditionLock = [[NSConditionLock alloc]init];
// 加鎖
[_conditionLock lock];
// 解鎖
[_conditionLock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_conditionLock tryLock];
/*
其他功能接口
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER; //初始化傳入條件
- (void)lockWhenCondition:(NSInteger)condition;//條件成立觸發(fā)鎖
- (BOOL)tryLockWhenCondition:(NSInteger)condition;//嘗試條件成立觸發(fā)鎖
- (void)unlockWithCondition:(NSInteger)condition;//條件成立解鎖
- (BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時間之內(nèi)
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//觸發(fā)鎖 條件成立 并且在等待時間之內(nèi)
*/
2.11 @synchronized()遞歸鎖、互斥鎖
// 初始化
@synchronized(鎖對象){
}
底層封裝的pthread_mutex的PTHREAD_MUTEX_RECURSIVE 模式,
鎖對象來表示是否為同一把鎖