iOS OC你了解的锁有哪些

什么是锁?
  • 在计算机科学中,锁是一种同步机制,用于在存在多线程的环境中实施对资源的访问限制。 就是在操作数据的时候,为了防止多个操作同时操作一个数据导致数据的错乱或者非即时而采用的一种规避手段。

常用的有以下几种:

1.@synchronized  互斥锁
2. NSLock 对象锁
3. dispatch_semaphore 信号量
4.NSCondition 条件锁
5.NSConditionLock
6.NSRecursiveLock 是递归锁

一.synchronized

synchronized是互斥锁,在这里主要考虑的是线程安全的问题,使用这个关键字,可以将一段代码限制在一个线程内使用,如果有一个线程正在使用这块资源,那么别的线程想要使用的时候就必须等待这快线程执行完毕!

首先 @synchronized() 小括号内需要一个参数,这个参数就表示信号量。这个参数可以是任何对象,包括 self,或者是自定义的信号量。针对不同的操作应该定义不同的信号量。

@synchronized() {…} 大括号中就是要加锁执行的代码,代码会操作一些数据。当开始执行代码时,意味着当前线程对其加锁了,当代码执行完后,自动解锁,其他线程才允许执行此段代码。

1.1优缺点

有点:能有效防止因多线程抢夺资源造成的数据安全问题。
缺点:需要消耗大量的CUP资源。

@interface ViewController ()

/** 票的总数 */
@property (nonatomic, assign) NSInteger ticketNumber;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.ticketNumber = 100;
    
     for (NSInteger i = 0; i < 10; i++) {
           NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(sellTicketsWithSynchronized) object:nil];
           [thread setName:[NSString stringWithFormat:@"售票员-%zd",i]];
           [thread start];
       }
    
}

- (void)sellTicketsWithSynchronized
{
    while (true) {
        @synchronized(self){
            if (self.ticketNumber > 0) {
                self.ticketNumber --;
                NSThread *thread = [NSThread currentThread];
                NSLog(@"%@卖了一张票,还剩%ld张票",[thread name],self.ticketNumber);
            }else{
                // 退出当前线程
                [NSThread exit];
            }
        }
    }
}
二. NSLock

NSLock 遵循 NSLocking 协议,lock 方法是加锁,unlock 是解锁,tryLock 是尝试加锁,如果失败的话返回 NO,lockBeforeDate: 是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。

@interface ViewController ()

/** 票的总数 */
@property (nonatomic, assign) NSInteger ticketNumber;
@property (strong, nonatomic) NSLock *lock;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.ticketNumber = 100;
    self.lock = [[NSLock alloc]init];
    
     for (NSInteger i = 0; i < 10; i++) {
           NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(sellTicketsWithNSLock) object:nil];
           [thread setName:[NSString stringWithFormat:@"售票员-%zd",i]];
           [thread start];
       }
    
}

- (void)sellTicketsWithNSLock
{
    while (true) {
        [self.lock lock];
        if (self.ticketNumber > 0) {
            self.ticketNumber --;
            NSThread *thread = [NSThread currentThread];
            NSLog(@"%@卖了一张票,还剩%ld张票",[thread name],self.ticketNumber);
        }else{
            // 退出当前线程
            [NSThread exit];
        }
         [self.lock unlock]; // 解锁
    }
}
三.dispatch_semaphore

dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数,分别是

//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)

//提高信号量
dispatch_semaphore_signal(信号量)

//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)

(1)dispatch_semaphore_create的声明为:
dispatch_semaphore_t dispatch_semaphore_create(long value);
传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。

(2)dispatch_semaphore_signal的声明为:
dispatch_semaphore_signal(semaphore);
这个函数会使传入的信号量semaphore的值加1;

(3) dispatch_semaphore_wait的声明为:
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
这个函数会使传入的信号量semaphore的值减1;这个函数的作用是这样的,如果semaphore信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果semaphore的值为0,那么这个函数就阻塞当前线程等待timeout

-(void)dispatchSignal{
    //crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);
    });
}

四. NSCondition

NSCondition 的对象实际上作为一个锁和一个线程检查器:锁主要为了当检测条件时保护数据源,执行条件引发的任务;线程检查器主要是根据条件决定是否继续运行线程,即线程是否被阻塞。
使用

NSConditon *condition =[ [NSCondition alloc]]init;

[condition lock];//一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只被访问、修改一次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问

[condition unlock];//与lock 同时使用

[condition wait];//让当前线程处于等待状态

[condition signal];//CPU发信号告诉线程不用在等待,可以继续执行
NSCondition *lock = [[NSCondition alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
  //线程1
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      [lock lock];
      while (!array.count) {
          [lock wait];
      }
      [array removeAllObjects];
      NSLog(@"array removeAllObjects");
      [lock unlock];
  });
  
  //线程2
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      [lock lock];
      [array addObject:@1];
      NSLog(@"array addObject:@1");
      [lock signal];
      [lock unlock];
  });
五. NSConditionLock

设计了一个例子,有一号售票窗口和二号售票窗口两个窗口可以买票,也会有一个退票窗口,但是退票窗口随机选择退到一号或者二号售票窗口。

NSConditionLock与NSCondition大体相同,但是NSConditionLock可以设置锁条件,而NSCondition确只是无脑的通知信号。

@interface ViewController ()

@property (nonatomic,strong) NSMutableArray *tickets;
@property (nonatomic,assign) int soldCount;
@property (nonatomic,strong) NSConditionLock *condition;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self forTest];
}

- (void)forTest
{
    self.tickets = [NSMutableArray arrayWithCapacity:1];
    self.condition = [[NSConditionLock alloc]initWithCondition:0];
    NSThread *windowOne = [[NSThread alloc]initWithTarget:self selector:@selector(soldTicketOne) object:nil];
    [windowOne start];
    
    NSThread *windowTwo = [[NSThread alloc]initWithTarget:self selector:@selector(soldTicketTwo) object:nil];
    [windowTwo start];
   
    NSThread *windowTuiPiao = [[NSThread alloc]initWithTarget:self selector:@selector(tuiPiao) object:nil];
    [windowTuiPiao start];

}
//一号窗口
-(void)soldTicketOne
{
    while (YES) {
        NSLog(@"====一号窗口没票了,等别人退票");
        [self.condition lockWhenCondition:1];
        NSLog(@"====在一号窗口买了一张票,%@",[self.tickets objectAtIndex:0]);
        [self.tickets removeObjectAtIndex:0];
        [self.condition unlockWithCondition:0];
    }
}
//二号窗口
-(void)soldTicketTwo
{
    while (YES) {
        NSLog(@"====二号窗口没票了,等别人退票");
        [self.condition lockWhenCondition:2];
        NSLog(@"====在二号窗口买了一张票,%@",[self.tickets objectAtIndex:0]);
        [self.tickets removeObjectAtIndex:0];
        [self.condition unlockWithCondition:0];
    }
}
- (void)tuiPiao
{
    while (YES) {
        sleep(3);
        [self.condition lockWhenCondition:0];
        [self.tickets addObject:@"南京-北京(退票)"];
        int x = arc4random() % 2;
        if (x == 1) {
            NSLog(@"====有人退票了,赶快去一号窗口买");
            [self.condition unlockWithCondition:1];
        }else
        {
            NSLog(@"====有人退票了,赶快去二号窗口买");
            [self.condition unlockWithCondition:2];
        }
    }
    
}
六.NSRecursiveLock

NSRecursiveLock 是递归锁,如果在递归中使用锁,可能会造成死锁的情况,但是若使用递归锁即可解决这个问题,递归锁允许一个线程多次请求锁,保证不会出现死锁的情况。

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

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