锁是用来保证线程安全同步工具,当一个线程在访问数据或资源前,需要先获取锁,如果此时该资源以被上锁,则该线程只能等待知道资源的锁被释放掉。
atomic并不能保证数据的安全,举个例子。
定义了一个数组属性arr(atomic):
//Thread A
for(int i = 0 ; i < 1000 ; i++){
if(i%2 == 0){
self.arr = @[@"1",@"2",@"3"];
}else{
self.arr = @[@"4",@"5"];
}
}
//Thread B
if(self.arr.count > 2){
NSString *str = [self.arr objectAtIndex:1];
}
虽然此时判断了数组长度大于2,大家可能觉得拿到的结果一定是2,但是有可能是5,因为线程A和B可能是同步的,此时便需要对self.arr加锁。
锁的分类:
自旋锁:
在线程等待时会一直轮询,处于忙等状态。但是减少了线程切换的开销。
自旋锁是不安全的,可能会出现忙等,因为优先级反转问题。什么是优先级反转,举个例子:
任务A(高优先级)现在需要访问资源Z;
此时任务C(低优先级)现在已经对资源Z上锁了,但是此时没有获取到时间片执行任务;
因为此时任务B(中优先级)占用着时间片在执行任务;
那么问题来了,当任务B执行完之后,C和A就会去抢占时间片执行任务,但是A的时间片比C的高,那么A获得时间片,他也没干啥正经事,一直轮询C的锁是否释放掉;
而C也一直在等待A把时间片交还给他,那么死循环就出现了。
解决优先级反转的方案:
1.优先级继承:
占有锁的任务可以继承等待该锁的线程最高优先级,这样就拥有了最高优先级,一旦时间片空闲,就可以获得时间片开始执行任务;
2.优先级天花板
给临界区设置一个最高优先级,一旦进入该临界区,那么将获得最高优先级。
3.禁止中断
只存在两种优先级,一种是可被抢占的,一种是禁止中断的。
进入临界区优先级为禁止中断的,其他的都是可被抢占的,就不存在互抢了。
互斥锁:互斥锁可分为递归和非递归锁
@synchonized:递归锁
NSLock:非递归锁
NSConditionLock条件锁,只有condition参数和初始化的condition相等,lock才能正确的进行加锁操作。