實現鎖的優勢就是咱們不須要在代碼中顯式的建立鎖對象,即可以實現鎖的機制git
@synchronized (self) {
//TODO:加鎖操做
}
複製代碼
互斥鎖是用來保證共享數據操做的完整性。每一個對象都對應於一個可稱爲「互斥鎖」的標記,這個標記用來保證在任一時刻,只能有一個線程訪問對象github
NSLock遵循NSLocking協議數組
@protocol NSLocking
- (void)lock;//加鎖
- (void)unlock;//解鎖
@end
複製代碼
NSLock鎖較爲經常使用,一般是添加在一個線程中,要注意的是添加鎖的線程不要是屢次執行的,由於添加鎖以後,其餘線程要等待鎖執行以後才能執行,因此添加鎖的的代碼最好是不耗時markdown
- (BOOL)tryLock;//嘗試加鎖
//指定Date以前嘗試加鎖,若是在指定時間以前都不能加鎖
- (BOOL)lockBeforeDate:(NSDate *)limit;
複製代碼
使用示例多線程
NSLock *lock = [NSLock new];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程1 嘗試加速ing...");
[lock lock];
sleep(3);//睡眠5秒
NSLog(@"線程1");
[lock unlock];
NSLog(@"線程1解鎖成功");
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程2 嘗試加速ing...");
BOOL x = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4]];
if (x) {
NSLog(@"線程2");
[lock unlock];
}else{
NSLog(@"失敗");
}
});
複製代碼
pthread除了建立互斥鎖,還能夠建立遞歸鎖、讀寫鎖、once等鎖async
- (void)lock{
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"+++++ 線程1 start");
pthread_mutex_lock(&mutex);
sleep(2);
pthread_mutex_unlock(&mutex);
NSLog(@"+++++ 線程1 end");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"----- 線程2 start");
pthread_mutex_lock(&mutex);
sleep(3);
pthread_mutex_unlock(&mutex);
NSLog(@"----- 線程2 end");
});
}
複製代碼
NSCondition 的對象其實是做爲一個鎖和線程檢查器,鎖主要是爲了檢測條件時保護數據源,執行條件引起的任務。線程檢查器主要是根據條件決定是否繼續運行線程,即線程是否被阻塞。函數
- (NSArray*)removeLastImage:(NSMutableArray *)images {
if (images.count > 0) {
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
[images removeLastObject];
[condition unlock];
return images.copy;
}else{
return NULL;
}
}
複製代碼
NSCondition能夠給每一個線程分別加鎖,加鎖後不影響其餘線程進入臨界區oop
- (void)testLock{
self.conditionArray = [NSMutableArray array];
self.condition = [[NSCondition alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.condition lock];
if (self.conditionArray.count == 0) {
NSLog(@"等待制做數組");
[self.condition wait];
}
NSLog(@"獲取對象進行操做:%@",self.conditionArray[0]);
[self.condition unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.condition lock];
id obj = @"xxxxxxx";
[self.conditionArray addObject:obj];
NSLog(@"建立了一個對象:%@",obj);
[self.condition signal];
[self.condition unlock];
});
}
複製代碼
NSCondition *cLock = [NSCondition new];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start");
[cLock lock];
[cLock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
NSLog(@"線程1");
[cLock unlock];
});
複製代碼
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];
NSLog(@"線程2");
[cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"喚醒一個等待的線程");
[cLock signal];
});
複製代碼
.........
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"喚醒全部等待的線程");
[cLock broadcast];
});
複製代碼
只有 condition 參數與初始化時候的 condition 相等,才能正確進行加鎖操做。而 unlockWithCondition: 並非當 Condition 符合條件時才解鎖,而是解鎖以後,修改 Condition 的值。性能
- (void)testLock{
NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0;i < 6;i++) {
[conditionLock lock];
NSLog(@"thread1:%d",i);
sleep(2);
[conditionLock unlockWithCondition:i];
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lockWhenCondition:2];
NSLog(@"thread2");
[conditionLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lockWhenCondition:3];
NSLog(@"thread3");
[conditionLock unlock];
});
}
複製代碼
NSRecursiveLock 是遞歸鎖,他和 NSLock 的區別在於,NSRecursiveLock 能夠在一個線程中重複加鎖(反正單線程內任務是按順序執行的,不會出現資源競爭問題),NSRecursiveLock 會記錄上鎖和解鎖的次數,當兩者平衡的時候,纔會釋放鎖,其它線程才能夠上鎖成功。spa
- (void)lock4 {
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^TestMethod)(int);
TestMethod = ^(int value) {
[recursiveLock lock];
if (value > 0) {
NSLog(@"加鎖層數: %d",value);
TestMethod(--value);
}
NSLog(@"程序退出!");
[recursiveLock unlock];
};
TestMethod(3);
});
}
複製代碼
- (void)lock6 {
__block pthread_mutex_t recursiveMutex;
pthread_mutexattr_t recursiveMutexattr;
pthread_mutexattr_init(&recursiveMutexattr);
pthread_mutexattr_settype(&recursiveMutexattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&recursiveMutex, &recursiveMutexattr);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^TestMethod)(int);
TestMethod = ^(int value) {
pthread_mutex_lock(&recursiveMutex);
if (value > 0) {
NSLog(@"加鎖層數: %d",value);
sleep(1);
TestMethod(--value);
}
NSLog(@"程序退出!");
pthread_mutex_unlock(&recursiveMutex);
};
TestMethod(3);
});
}
複製代碼
是用於多線程同步的一種鎖,線程反覆檢查鎖變量是否可用。因爲線程在這一過程當中保持執行,所以是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,直至顯式釋放自旋鎖。 自旋鎖避免了進程上下文的調度開銷,所以對於線程只會阻塞很短期的場合是有效的。
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程1 準備上鎖");
OSSpinLockLock(&oslock);
sleep(4);
NSLog(@"線程1");
OSSpinLockUnlock(&oslock);
NSLog(@"線程1 解鎖成功");
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程2 準備上鎖");
OSSpinLockLock(&oslock);
NSLog(@"線程2");
OSSpinLockUnlock(&oslock);
NSLog(@"線程2 解鎖成功");
});
複製代碼
若是獲取不到鎖,會將當前線程阻塞、休眠,直到其餘線程釋放鎖時,會喚醒當前線程。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task A");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task B");
dispatch_semaphore_signal(semaphore);
});
複製代碼
NSDistributedLock *lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/lock.lock"];
while (![lock tryLock]){
sleep(1);
}
//do something
[lock unlock];
複製代碼
死鎖是因爲多個線程(進程)在執行過程當中,由於爭奪資源而形成的互相等待現象,你能夠理解爲卡主了。產生死鎖的必要條件有四個:
NSLock *rLock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^TestBlock)(int);
TestBlock = ^(int value) {
[rLock lock];
if (value > 0) {
NSLog(@"線程%d", value);
TestBlock(value - 1);
}
[rLock unlock];
};
TestBlock(4);
});
複製代碼
最多見的就是 同步函數 + 主隊列 的組合,本質是隊列阻塞。
死鎖是因爲阻塞閉環形成的,那麼咱們只用消除其中一個因素,就能打破這個閉環,避免死鎖。
理解GCD死鎖
iOS 中幾種經常使用的鎖總結
iOS 中常見的幾種鎖-代碼示例
iOS進階-細數iOS中的鎖