按照功能來(lái)區(qū)分鎖:
互斥鎖(mutexlock)sleep-waiting:
保證共享數(shù)據(jù)操作的完整性, 鎖被占用的時(shí)候會(huì)休眠, 等待鎖釋放的時(shí)候會(huì)喚醒经窖。
在訪問(wèn)共享資源之前進(jìn)行加鎖主守,訪問(wèn)完成后解鎖框杜。
線程A獲取到鎖,在釋放鎖之前塑陵,其他線程都獲取不到鎖傻工。
解鎖時(shí)鲜戒,如果有1個(gè)以上的線程阻塞师幕,那么所有該鎖上的線程變?yōu)榫途w狀態(tài)粟按,第一個(gè)就緒的加鎖,其他的又進(jìn)入休眠们衙。
從而實(shí)現(xiàn)在任意時(shí)刻钾怔,最多只有1個(gè)線程能夠訪問(wèn)被互斥鎖保護(hù)的資源。
自旋鎖(spinlock)busy-waiting:
跟互斥鎖類似, 只是資源被占用的時(shí)候, 會(huì)一直循環(huán)檢測(cè)鎖是否被釋放(CPU不能做其他的事情)
線程A獲取到鎖蒙挑,在釋放鎖之前,線程B 來(lái)獲取鎖愚臀,此時(shí)獲取不到忆蚀,線程B就會(huì)不斷的進(jìn)入循環(huán),一直檢查鎖是否已被釋放姑裂,如果釋放馋袜,則能獲取到鎖。
由于調(diào)用方會(huì)一直循環(huán)看該自旋鎖的的保持者是否已經(jīng)釋放了資源舶斧,節(jié)省了喚醒睡眠線程的內(nèi)核消耗欣鳖。
但是如果不能短時(shí)間獲得鎖,就會(huì)一直占用著CPU茴厉,造成效率低下泽台。
(在加鎖時(shí)間短暫的情況下會(huì)大大提高效率)
其他鎖都是這兩種鎖的延伸和擴(kuò)展。
常見(jiàn)的鎖
1矾缓、OSSpinLock
自旋鎖怀酷,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)的問(wèn)題。當(dāng)一個(gè)低優(yōu)先級(jí)線程獲得鎖的時(shí)候嗜闻,如果此時(shí)一個(gè)高優(yōu)先級(jí)的系統(tǒng)到來(lái)蜕依,那么會(huì)進(jìn)入忙等狀態(tài),不會(huì)進(jìn)入睡眠琉雳,此時(shí)會(huì)一直占用著系統(tǒng)CPU時(shí)間样眠,導(dǎo)致低優(yōu)先級(jí)的無(wú)法拿到CPU時(shí)間片,從而無(wú)法完成任務(wù)也無(wú)法釋放鎖翠肘。除非能保證訪問(wèn)鎖的線程全部處于同一優(yōu)先級(jí)檐束,否則系統(tǒng)所有的自旋鎖都會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)的問(wèn)題。
現(xiàn)在蘋(píng)果的OSSpinLockiOS10+已廢棄锯茄,已經(jīng)被替換成os_unfair_lock厢塘。茶没,有興趣可以看看:不再安全的OSSpinLock。
2晚碾、os_unfair_lock
互斥鎖抓半,iOS10+才支持,為了替代OSSpinLock格嘁。
os_unfair_lock 是一種適用于需要簡(jiǎn)單和輕量級(jí)互斥的情況下的鎖笛求。
此鎖必須從與鎖定它的相同線程中解鎖,嘗試從不同線程解鎖將導(dǎo)致斷言中止進(jìn)程糕簿。
必須使用 OS_UNFAIR_LOCK_INIT 進(jìn)行初始化探入。使用時(shí)需要引入#import <os/lock.h>
// 必須在線程里初始化 且必須使用OS_UNFAIR_LOCK_INIT初始化
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock); // 加鎖
......其他操作......
os_unfair_lock_unlock(unfairLock); // 解鎖
3、pthread_mutex
互斥鎖懂诗,等待鎖的線程會(huì)處于休眠狀態(tài)蜂嗽。
使用時(shí)需要引入頭文件#import <pthread.h>
// 定義互斥鎖和互斥鎖屬性對(duì)象
// 若不特殊設(shè)置互斥鎖屬性對(duì)象可以不設(shè)置
// 初始化互斥鎖時(shí)的第二個(gè)參數(shù)可以傳NULL
pthread_mutex_t mutex;
pthread_mutexattr_t mutex_attr;
// 初始化互斥鎖屬性對(duì)象
pthread_mutexattr_init(&mutex_attr);
// 設(shè)置互斥鎖屬性為默認(rèn)類型
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_DEFAULT);
// 初始化互斥鎖
pthread_mutex_init(&mutex, &mutex_attr);
// 加鎖
pthread_mutex_lock(&mutex);
// 訪問(wèn)共享資源
// 這里省略了共享資源的操作
// 解鎖
pthread_mutex_unlock(&mutex);
// 銷毀互斥鎖
pthread_mutex_destroy(&mutex);
// 銷毀互斥鎖屬性對(duì)象
pthread_mutexattr_destroy(&mutex_attr);
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
互斥鎖類型具體為:
PTHREAD_MUTEX_NORMAL:普通鎖。默認(rèn)類型殃恒。不提供死鎖檢測(cè)或遞歸保護(hù)植旧。如果同一線程嘗試再次獲取鎖,可能會(huì)導(dǎo)致死鎖离唐。
PTHREAD_MUTEX_ERRORCHECK:錯(cuò)誤檢查鎖病附。提供死鎖檢測(cè)。如果同一線程嘗試再次獲取鎖亥鬓,函數(shù)將返回錯(cuò)誤 EBUSY完沪。
PTHREAD_MUTEX_RECURSIVE:遞歸鎖。允許同一線程多次獲取鎖而不會(huì)死鎖嵌戈。必須在解鎖時(shí)與加鎖次數(shù)匹配覆积。
PTHREAD_MUTEX_DEFAULT:等同于 PTHREAD_MUTEX_NORMAL。
4咕别、NSLock
互斥鎖技健,遵循NSLocking協(xié)議。內(nèi)部是封裝了pthread_mutext惰拱,類型是PTHREAD_MUTEXT_ERRORCHECK雌贱。
NSLock、NSCondition偿短、NSConditionLock欣孤、NSRecursiveLock都實(shí)現(xiàn)了NSLocking協(xié)議。了解更多查看官方文檔
@protocol NSLocking
- (void)lock;
- (void)unlock ;
@end
-lock和-unlock必須在相同的線程調(diào)用昔逗。
5降传、NSCondition
互斥鎖,也遵循NSLocking協(xié)議勾怒。內(nèi)部是封裝了pthread_mutext婆排,和它和NSLock的區(qū)別是:NSLock在獲取不到鎖的時(shí)候自動(dòng)使線程進(jìn)入休眠声旺,鎖被釋放后線程又自動(dòng)被喚醒,NSCondition可以使我們更加靈活的控制線程狀態(tài)段只,在任何需要的時(shí)候使線程進(jìn)入休眠或喚醒它腮猖。
// 讓當(dāng)前線程處于等待狀態(tài)
- (void)wait;
// 向等待在條件上的一個(gè)線程發(fā)出信號(hào),可以繼續(xù)執(zhí)行
- (void)signal;
// 向等待在條件上的所有線程發(fā)出信號(hào)赞枕,從而喚醒所有線程
- (void)broadcast;
NSCondition *cLock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lock];
NSLog(@"線程1加鎖成功");
[cLock wait];
NSLog(@"線程1");
[cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lock];
NSLog(@"線程2加鎖成功");
[cLock wait];
sleep(1);
NSLog(@"線程2");
[cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"喚醒一個(gè)等待的線程");
[cLock signal];
});
輸出:
線程1加鎖成功
線程2加鎖成功
喚醒一個(gè)等待的線程
線程1
6澈缺、NSConditionLock
互斥鎖,有條件的互斥鎖炕婶。
// 只讀屬性condition姐赡,保存鎖當(dāng)前的條件
@property (readonly) NSInteger condition;// 解鎖并設(shè)置新的條件值。
-(void)unlockWithCondition:(NSInteger)condition;
// 條件值滿足時(shí)加鎖, 執(zhí)行之后代碼
-(BOOL)lockWhenCondition:(NSInteger)condition;
// 當(dāng)條件值等于指定值時(shí)嘗試加鎖柠掂。
-(BOOL)tryLockWhenCondition:(NSInteger)condition;
NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0]; // 設(shè)置初始標(biāo)志位
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cLock tryLockWhenCondition:0]) {
NSLog(@"Task - 1, cLock.condition = %ld",cLock.condition);
[cLock unlockWithCondition:1];
NSLog(@"Task - 1, end - cLock.condition = %ld",cLock.condition);
} else {
NSLog(@"Task - 加鎖失敗");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:3];
NSLog(@"Task - 2, cLock.condition = %ld",cLock.condition);
[cLock unlockWithCondition:2];
NSLog(@"Task - 2, end- cLock.condition = %ld",cLock.condition);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:1];
NSLog(@"Task - 3, cLock.condition = %ld",cLock.condition);
[cLock unlockWithCondition:3];
NSLog(@"Task - 3, end- cLock.condition = %ld",cLock.condition);
});
輸出:
Task - 1, cLock.condition = 0
Task - 1, end - cLock.condition = 1
Task - 3, cLock.condition = 1
Task - 3, end- cLock.condition = 3
Task - 2, cLock.condition = 3
Task - 2, end- cLock.condition = 2
7项滑、NSRecursiveLock
互斥鎖中的遞歸鎖。內(nèi)部使用的pthread_mutex_t的類型是PTHREAD_MUTEXT_RECURSIVE陪踩≌让牵可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖肩狂。這主要是用在循環(huán)或遞歸操作中。
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
NSLock *lo = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
NSLog(@"加鎖: %d", value);
[lock lock]; // 加鎖
if (value < 3) {
sleep(2);
RecursiveMethod(++value); // 解鎖之前又鎖上
}
NSLog(@"解鎖: %d", value);
[lock unlock]; // 解鎖
};
RecursiveMethod(1);
NSLog(@"finish");
});
輸出:
加鎖: 1
加鎖: 2
加鎖: 3
解鎖: 3
解鎖: 3
解鎖: 2
finish
8姥饰、@synchronized
@synchronized(id)的使用應(yīng)該是較多的傻谁,它底層實(shí)現(xiàn)是個(gè)遞歸鎖,不會(huì)產(chǎn)生死鎖列粪,且不需要手動(dòng)去加鎖解鎖审磁,使用起來(lái)比較方便。
@synchronized(self) {
//數(shù)據(jù)操作
}
9岂座、atomic
修飾屬性關(guān)鍵字态蒂,對(duì)被修飾的進(jìn)行原子操作(不負(fù)責(zé)使用,只是setter/getter方法安全费什,其他不保證)點(diǎn)擊屬性關(guān)鍵字-atomic了解更多钾恢。
10、dispatch_semaphore_t
是GCD用來(lái)同步的一種方式鸳址。
GCD 信號(hào)量dispatch_semaphore可以用來(lái)控制最大并發(fā)數(shù)量瘩蚪,可以用來(lái)實(shí)現(xiàn) iOS 的線程同步方案。
信號(hào)量的初始值稿黍,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量疹瘦;
信號(hào)量的初始值為1,代表同時(shí)只允許 1 條線程訪問(wèn)資源巡球,保證線程同步言沐。
若設(shè)置信號(hào)量初始值為3.則控制最大并發(fā)數(shù)為3邓嘹。// 創(chuàng)建一個(gè) Semaphore 并初始化信號(hào)的總量
dispatch_semaphore_create// 發(fā)送一個(gè)信號(hào),讓信號(hào)總量加 1
dispatch_semaphore_signal// 可以使總信號(hào)量減 1险胰,信號(hào)總量小于 0 時(shí)就會(huì)一直等待(阻塞所在線程)汹押,否則就可以正常執(zhí)行。
dispatch_semaphore_wait
點(diǎn)擊iOS:多線程鸯乃,了解更多信號(hào)量相關(guān)信息鲸阻。
鎖的關(guān)系及性能
鎖之間的關(guān)系
性能排序(從高到低):
1、os_unfair_lock
2缨睡、OSSpinLock(已經(jīng)不推薦使用)
3鸟悴、dispatch_semaphore
4、pthread_mutext
5奖年、NSLock
6细诸、NSCondition
7、NSRecursiveLock
8陋守、NSConditonLock
9震贵、@synchronized
死鎖
指多個(gè)進(jìn)程因競(jìng)爭(zhēng)共享資源而造成的一種僵局,若無(wú)外力作用水评,這些進(jìn)程都將永遠(yuǎn)不能再向前推進(jìn)猩系。
死鎖的條件
1、互斥:某種資源一次只允許一個(gè)進(jìn)程訪問(wèn)中燥,即該資源一旦分配給某個(gè)進(jìn)程寇甸,其他進(jìn)程就不能再訪問(wèn),直到該進(jìn)程訪問(wèn)結(jié)束疗涉。
2拿霉、占有且等待:一個(gè)進(jìn)程本身占有資源(一種或多種),同時(shí)還有資源未得到滿足咱扣,正在等待其他進(jìn)程釋放該資源绽淘。
3、不可搶占:別人已經(jīng)占有了某項(xiàng)資源闹伪,你不能因?yàn)樽约阂残枰撡Y源沪铭,就去把別人的資源搶過(guò)來(lái),只能在使用完時(shí)由自己釋放祭往。
4伦意、循環(huán)等待:存在一個(gè)進(jìn)程鏈,使得每個(gè)進(jìn)程都占有下一個(gè)進(jìn)程所需的至少一種資源硼补。