iOS中的锁

锁是一种同步机制,用于多线程环境中对资源访问的限制
iOS中常见锁的性能对比图(摘自:ibireme):

lock_benchmark.png

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(@"线程二开始");
}
});
}
执行结果:
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()指令的优点是我们不需要显示创建锁对象,便可以方便实现锁机制。但作为一种防护措施,@synchronized块会隐式添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时自动释放互斥锁。如果不想隐式的异常处理例程消耗额外的资源,可以使用对象锁。
@synchronized(obj){}内部obj被释放或者被置为nil不会影响锁的功能,如果obj一开始就被置为nil,那么就会丢失锁的功能
2.NSLock
从NSLock类的.h文件中可以看出,NSLock、NSConditionLock、NSRecursiveLock、NSCondition都遵循NSLocking 协议:
NSLock实现了最基础的互斥锁,通过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加锁失败");
}
});

}
执行结果:
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,都会执行后续代码
3.lockBeforeDate:会在指定时间之前尝试加锁,会阻塞线程,如果在指定时间之前能加锁返回YES,在指定时间之前都不能加锁返回NO
4.由于是互斥锁,当一个线程进行访问的时候,该线程获取到锁,其他线程进行访问的时候,被系统挂起,直到该线程释放锁,其他线程才能对其进行访问,从而确保线程安全。如果连续锁定两次,则会造成死锁问题。
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);
});
}
执行结果:
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代码就不会被执行,就不能解锁,一直阻塞线程,造成死锁。在这种情况下,递归锁就能很好的解决这些问题。
4.NSConditionLock
NSConditionLock对象所定义的互斥锁可以在某些条件下进行加锁和解锁,和NSLock类似,也遵循NSLocking协议,方法也类似,多了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加锁失败");
    }
    
});

}
执行结果:
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,都会执行各自的后续操作;lockWhenCondition: beforeDate: 在指定的时间之前尝试加锁,会阻塞线程,如果在指定时间之前满足condition条件加锁成功返回YES,超过指定时间返回NO,执行后续操作。
5.NSCondition
NScondition是一种特殊类型的锁,通过它可以实现不同线程的调度。一个线程被某一个条件所阻塞,直到另一个线程满足该条件从而发信号给该线程使该线程可以正常运行。例如有一个下载图片的线程,一个处理图片的线程,当没有图片处理时处理图片的线程就会阻塞,当下载图片的线程下载完图片时满足了处理图片线程的需求,这个时候下载线程给处理线程一个信号,处理线程就会恢复运行。
注意:NSCondition对象实际上是一个锁和一个线程检查器,锁上之后其他线程也可以上锁,而之后根据条件决定是否继续运行线程,即线程是否要进入waiting状态。如果进入waiting状态,其他线程的该锁执行signal或者broadcast方法,线程被唤起,继续运行。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];
});

}
执行结果:
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:来阻塞线程,直到条件对象发出信号或者超时才会执行后续操作。
2.signal和broadcast的区别是:signal是一个信号量,只能唤起一个等待的线程,要想唤起多个线程必须多次调用,而broadcast可以唤起所有等待的线程。
3.signal或者broadcast要想唤起线程必须要有唤醒对象解锁,否则无法唤起,线程被唤起后必须有解锁操作,否则之后无法唤起其他线程,其他线程也无法加锁。
6.dispatch_semaphore
dispatch_semaphore使用信号量机制实现锁,等待信号和发送信号。
dispatch_semaphore是GCD用于同步的一种方式,与他相关的方法有三个,一个是创建信号,一个是等待信号,一个是发送信号。
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);
});
}
执行结果:
-(void)testDispatch_semaphore{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_time_t overtime = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC
4);
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只能发送,不能保存(如果没有线程在等待,则发送的信号失效)。而dispatch_semaphore能保存发送的信号。dispatch_semaphore的核心是dispatch——semaphore_t类型的信号量;
2.dispatch_semaphore_create(1)方法可以创建一个dispatch_semaphore_t类型的信号量,设定信号量的初始值为1。注意这里的传入参数必须大于或者等于0,否则dispatch_semaphore_create会返回NULL。
3.dispatch_semaphore_wait(semaphore,overTime);方法会判断semaphore的信号值是否大于0.大于0不会阻塞线程,消耗掉一个信号,执行后续任务。如果信号量为0,该线程会和NScondition一样直接进入waiting状态,等待其他线程发送信号唤醒线程去执行后续任务,或者当overTime时限到了,也会执行后续任务。
4.dispatch_semaphore_signal(semaphore);发送信号,如果没有等待的线程接受信号,则使signal信号值加一(做到对信号的保存)。
5.一个dispatch_semaphore_wait(semaphore,overTime);方法会去对应一个dispatch_semaphore_singal(semaphore);看起来像NSLock的lock和unlock,其实可以这样理解,区别只在于信号量这个参数,lock unlcok只能同意时间,一个线程访问被保护的临界区,而如果dispatch_semaphore的信号量的初始值为x,则可以有x个线程同时访问被保护的临界区。
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);
});

}
执行结果:
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芙蓉区别在于,tryLcok返回的是YES和NO,pthread_mutex_trylock加锁成功返回的是0,失败返回的是错误提示码。
2.pthread_mutex(&recursive)作用和NSRecursiveLock递归锁类似,如果使用pthread_mutex_init(&theLock,NULL);初始化锁的画,上面的代码的第二部分就会出现死锁现象,使用递归锁就可以避免这种现象。
8.OSSpinLock
OSSpinLock是一种自旋锁,和互斥锁类似,都是为了保证线程安全的锁。大门二者的区别是不一样的,对于互斥锁,当一个线程获得这个锁之后,其他想要获得所的线程将会被阻塞,直到该锁被释放。但是自旋锁不一样,当一个线程获得锁之后,其他线程将会一直循环在哪里查看该锁是否被释放,所以此锁比较适用于锁的持有者保存时间较短的情况下。
代码:
-(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结束");
});
}
执行结果:
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的文章阐述了自旋锁存在优先级反转问题,具体文章可以点击此处而OSSpinLock在iOS10.0中被<os/lock.h>中的os_unfair_lock取代了。
9.os_unfair_lock
这个锁就是OSSpinLock的替代,用来解决优先级反转问题。
代码:
-(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结束");
});
}
执行结果:
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.每一种锁的执行步骤基本都是加锁、等待、解锁。
2.@synchronized()执行效率最低,但是使用起来方便,如果没有什么性能瓶颈可以使用。
3.OSSpinLock的效率最高,但是存在优先级反转问题不安全,iOS 10之后出了os_unfair_lock来代替OSSpinLock,dispatch_semaphore和pthread_mutex的执行效率紧随其后,既可以保证效率又可以保证安全是比较好的选择。
4.遵循NSLocking的锁,执行效率为NSLock>NSCondition>NSRecursiveLock>NSConditionLock

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 原文地址 1 前言 近日工作不是太忙,刚好有时间了解一些其他东西,本来打算今天上午去体检,但是看看天气还是明天再去...
    幸福的鱼阅读 352评论 0 0
  • 在多线程操作过程中,往往一个数据同时被多个线程读写,在这种情况下,如果没有相应的机制对数据进行保护,就很可能会发...
    没技术的BUG开发攻城狮阅读 361评论 0 0
  • 在平时的开发中经常使用到多线程,在使用多线程的过程中,难免会遇到资源竞争的问题,那我们怎么来避免出现这种问题那? ...
    IAMCJ阅读 3,033评论 2 25
  • 2016年国庆假期终于把此书过完,整理笔记和体会于此。 关于书名 书名源于俄罗斯的演员斯坦尼斯拉夫斯基创作的《演员...
    李剑飞的简书阅读 7,126评论 2 65
  • 你有见过这样的天吗,它苍白,然劲俏,隐隐看得见有淡蓝匿了去,偶尔飞鸟掠过。你撑伞,细雨有声,在伞面,听来着实欢快。...
    uilyacho阅读 152评论 0 0