线程锁

作者: 鐧箪 | 来源:发表于2019-07-05 17:25 被阅读0次

多线程编程中,应该尽量避免资源在线程之间共享,以减少线程间的相互作用。 但是总是有多个线程相互干扰的情况(如多个线程访问一个资源)。在线程必须交互的情况下,就需要一些同步工具,来确保当它们交互的时候是安全的。

我们在声明属性的时候 一个名词用的很频繁 atomic(原子性操作) 、 nonatomic(非原子性操作)
atomic 提供多线程安全 会有加锁操作 平时的操作因为不会涉及到多线程资源争夺 所以 用atomic 显的有些浪费 所以我们都用nonatomic 没有任何限制

下面我们来看实例吧

dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        self.price = @"1000";
        NSLog(@"你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张");
        NSLog(@"你好 现在杭州到北京的票价%@",self.price);
        NSLog(@"好的帮我预订一张");
        NSLog(@"好的先生我现在帮你处理订单");
        sleep(2);
        NSLog(@"操作好了请用支付宝或微信支付");
        NSLog(@"您已支付完毕 您购买的机票金额为%@元",self.price);
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        sleep(2);
        self.price = @"1200";
    });
线程1 ----------<NSThread: 0x600001fe55c0>{number = 4, name = (null)}
线程2 ----------<NSThread: 0x600001fdaa00>{number = 3, name = (null)}
你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张
你好 现在杭州到北京的票价1000
好的帮我预订一张
好的先生我现在帮你处理订单
操作好了请用支付宝或微信支付
您已支付完毕 您购买的机票金额为1200元

平时我们买飞机票 可以通过不同途径购买 飞机票的价格也是随着剩余的票数 决定打折程度 上面发送的问题是 票价为1000的机票 实际支付完成的时候确实1200

通过代码我们发现 异步并发的两个子线程 都是操作price 价格 导致了 一个线程在使用price 的时候发生价格变动 这显然是违背我们的主观意愿的 为了防止这种变动 我们需要用到线程锁来处理

iOS的线程锁有很多种 接下来我们来逐一介绍

@synchronized
@synchronized (self) {
        //需要加锁的写在里面
    }

synchronized中传入的object的内存地址,被用作key,通过hash map对应的一个系统维护的递归锁。不管是传入什么类型的object,只要是有内存地址,就能启动同步代码块的效果

@synchronized是最方便的 但是它是最慢的

dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        @synchronized (self) {
            self.price = @"1000";
            NSLog(@"你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张");
            NSLog(@"你好 现在杭州到北京的票价%@",self.price);
            NSLog(@"好的帮我预订一张");
            NSLog(@"好的先生我现在帮你处理订单");
            sleep(2);
            NSLog(@"操作好了请用支付宝或微信支付");
            NSLog(@"您已支付完毕 您购买的机票金额为%@元",self.price);
        }
    });

dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
       //保证线程2 在线程1后加锁
        sleep(2);
        @synchronized (self) {
            self.price = @"1200";
            NSLog(@"当前杭州到北京的最新价格为%@",self.price);
        }
    });
你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张
你好 现在杭州到北京的票价1000
好的帮我预订一张
好的先生我现在帮你处理订单
操作好了请用支付宝或微信支付
您已支付完毕 您购买的机票金额为1000元
当前杭州到北京的最新价格为1200
NSLock

遵循 NSLocking 协议 所以具备 lock(加锁) unlock(解锁) 两个要成对使用

lock 会阻塞当前线程 直到加锁成功

dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        [lock lock];
        self.price = @"1000";
        NSLog(@"你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张");
        NSLog(@"你好 现在杭州到北京的票价%@",self.price);
        NSLog(@"好的帮我预订一张");
        NSLog(@"好的先生我现在帮你处理订单");
        sleep(2);
        NSLog(@"操作好了请用支付宝或微信支付");
        NSLog(@"您已支付完毕 您购买的机票金额为%@元",self.price);
        [lock unlock];
    });

dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        //保证线程2 在线程1后加锁
        sleep(2);
        [lock lock];
        self.price = @"1200";
        NSLog(@"当前杭州到北京的最新价格为%@",self.price);
        [lock unlock];
    });

尝试加锁 加锁成功返回YES 需要解锁 加锁失败 返回NO

- (BOOL)tryLock;
dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        sleep(2);
        if ([lock tryLock]) {
            self.price = @"1200";
            NSLog(@"当前杭州到北京的最新价格为%@",self.price);
        }else {
            NSLog(@"当前票价没变化");
        }
        
    });
当前票价没变化

输出 当前票价没变化之后方法执行完毕 没有尝试继续加锁 不会阻塞当前线程

在指定时间内尝试加锁

- (BOOL)lockBeforeDate:(NSDate *)limit;
dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        sleep(2);
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
            self.price = @"1200";
            NSLog(@"当前杭州到北京的最新价格为%@",self.price);
        }else {
            NSLog(@"当前票价没变化");
        }
        
    });
当前杭州到北京的最新价格为1200

会阻塞当前线程 并且在指定时间内 反复尝试加锁

NSConditionLock

条件锁 多个condition

//初始化 条件为0
    NSConditionLock * lock = [[NSConditionLock alloc] initWithCondition:50];
    
    dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        NSLog(@"1号犯人越狱中");
        [lock lockWhenCondition:1];
        NSLog(@"1号犯人越狱成功");
        [lock unlock];
    
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        NSLog(@"2号犯人越狱中");
        [self archiveKey:lock];
       
    });
    
    dispatch_async(queue, ^{
        NSLog(@"线程3 ----------%@",[NSThread currentThread]);
        NSLog(@"3号犯人越狱中");
        [lock lockWhenCondition:3];
        NSLog(@"3号犯人越狱成功");
        [lock unlockWithCondition:4];
        
    });
    
    dispatch_async(queue, ^{
        NSLog(@"线程4 ----------%@",[NSThread currentThread]);
        NSLog(@"4号犯人越狱中");
        [lock lockWhenCondition:4];
        NSLog(@"4号犯人越狱成功");
        [lock unlockWithCondition:1];
        
    });
-(void)archiveKey:(NSConditionLock *)lock {
    NSLog(@"2号犯人开始越狱");
    NSInteger r = arc4random_uniform(100);
    if ([lock tryLockWhenCondition:r]) {
        NSLog(@"2号犯人反复尝试后终于拿到了监狱的钥匙 越狱成功");
        [lock unlockWithCondition:3];
    }else {
        NSLog(@"2号犯人越狱失败");
        [self archiveKey:lock];
    }
}
2号犯人反复尝试后终于拿到了监狱的钥匙 越狱成功
3号犯人越狱成功
4号犯人越狱成功
1号犯人越狱成功

条件锁 condtion 初始化可以默认一个值 加锁 、解锁 成功 都可以给condtion指定一个值 加锁失败 、不设置condtion 不变

通过 condition 我们可以实现线程依赖

NSRecursiveLock

递归锁 当前线程可以反复上锁不会导致死锁

NSLock * lock = [[NSLock alloc] init];
    NSRecursiveLock * relock = [[NSRecursiveLock alloc] init];
    dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        [lock lock];
        [lock lock];
        NSLog(@"1号犯人越狱成功");
        [lock unlock];
        [lock unlock];
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        [relock lock];
        [relock lock];
        NSLog(@"2号犯人越狱成功");
        [relock unlock];
        [relock unlock];
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程3 ----------%@",[NSThread currentThread]);
        sleep(5);
        [relock lock];
        NSLog(@"3号犯人越狱成功");
        [relock unlock];
    });
2号犯人越狱成功
3号犯人越狱成功

可以看到 NSLock 因为同一个线程上了2次锁 已经造成死锁 不会有任何输出
NSRecursiveLock 可以同线程多次上锁 如同引用计数 会记录上锁的次数 当解锁次数相同的时候才会释放掉 才不会影响其他线程上锁。

NSCondition

线程检测器 不需要轮询 直接可以让当前线程等待 不影响其他线程上锁

NSCondition  * condition = [[NSCondition alloc] init];
    dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    __block BOOL finished = NO;
    dispatch_async(queue, ^{
        [condition lock];
        
        if (!finished) {
            [condition wait];
        }
        
        NSLog(@"1号任务执行完毕");
        [condition unlock];
        
        
    });
    
    
    dispatch_async(queue, ^{
        sleep(2);
        [condition lock];
        sleep(10);
        NSLog(@"2号任务执行完毕");
        finished = YES;;
        [condition signal];
        [condition unlock];
        
    });  
2号任务执行完毕
1号任务执行完毕
OSSpinLock
typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock);

bool    OSSpinLockTry( volatile OSSpinLock *__lock );

void    OSSpinLockLock( volatile OSSpinLock *__lock );

void    OSSpinLockUnlock( volatile OSSpinLock *__lock );

自旋锁 同NSLock 不同的是 NSLock 轮询过后 会进入waiting 等待唤醒 OSSpinLock 会一直轮询 不会进入waiting 比较耗资源 但是速度快

__block OSSpinLock spinLock  = OS_SPINLOCK_INIT;
    dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        OSSpinLockLock(&spinLock);
        self.price = @"1000";
        NSLog(@"你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张");
        NSLog(@"你好 现在杭州到北京的票价%@",self.price);
        NSLog(@"好的帮我预订一张");
        NSLog(@"好的先生我现在帮你处理订单");
        sleep(10);
        NSLog(@"操作好了请用支付宝或微信支付");
        NSLog(@"您已支付完毕 您购买的机票金额为%@元",self.price);
        OSSpinLockUnlock(&spinLock);
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        sleep(2);
        OSSpinLockLock(&spinLock);
        self.price = @"1200";
        NSLog(@"当前杭州到北京的最新价格为%@",self.price);
        OSSpinLockUnlock(&spinLock);
    });

效果同上

'OSSpinLock' is deprecated: first deprecated in iOS 10.0 - Use os_unfair_lock() from <os/lock.h> instead

你会发现 会有一个警告 因为OSSpinLock 有BUG存在 不再安全 所以推荐使用 os_unfair_lock

 __block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
 
    dispatch_queue_t queue = dispatch_queue_create("bf", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"线程1 ----------%@",[NSThread currentThread]);
        os_unfair_lock_lock(&unfairLock);
        self.price = @"1000";
        NSLog(@"你好我要买一张机票 请问现在杭州到北京的票现在多少钱一张");
        NSLog(@"你好 现在杭州到北京的票价%@",self.price);
        NSLog(@"好的帮我预订一张");
        NSLog(@"好的先生我现在帮你处理订单");
        sleep(10);
        NSLog(@"操作好了请用支付宝或微信支付");
        NSLog(@"您已支付完毕 您购买的机票金额为%@元",self.price);
        os_unfair_lock_unlock(&unfairLock);
    });
    
    
    dispatch_async(queue, ^{
        NSLog(@"线程2 ----------%@",[NSThread currentThread]);
        sleep(2);
        os_unfair_lock_lock(&unfairLock);
        self.price = @"1200";
        NSLog(@"当前杭州到北京的最新价格为%@",self.price);
        os_unfair_lock_unlock(&unfairLock);
     
    });
pthread_mutex_t

pthread_mutex_t 可以创建互斥锁 检错锁 递归锁

int pthread_mutex_init(pthread_mutex_t * __restrict,
        const pthread_mutexattr_t * _Nullable __restrict)

pthread_mutexattr_t 可以指定锁的类型 默认为互斥锁可以传NULL

#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 、PTHREAD_MUTEX_DEFAULT 默认为互斥锁 保证一次只有一个线程在执行 如果多次锁 会造成死锁

PTHREAD_MUTEX_ERRORCHECK 检错锁 同一个线程 加锁 第一次成功返回0 如果不解锁 后面再加锁则返回非0 不会造成死锁

PTHREAD_MUTEX_RECURSIVE 递归锁 同一个线程可以反复加锁 不会形成死锁 当然要全部解锁掉 才不会影像其他线程

static pthread_mutex_t mutex_lock;  
pthread_mutexattr_t attr;
    pthread_mutexattr_init (&attr);
    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
 pthread_mutex_init(&mutex_lock, &attr);
pthread_mutexattr_destroy (&attr);
    
    
    pthread_t thread;
    pthread_create(&thread, NULL, methord, NULL);
    
    pthread_t otherThread;
    pthread_create(&otherThread, NULL, otherMethord, NULL);

void * methord() {
    NSLog(@"线程1 ----------%@",[NSThread currentThread]);
    
   int a = pthread_mutex_lock(&mutex_lock);
   int b =  pthread_mutex_lock(&mutex_lock);
NSLog(@"b------%d",b);
    sleep(6);
    NSLog(@"1号开始活动");
    pthread_mutex_unlock(&mutex_lock);
    pthread_mutex_unlock(&mutex_lock);
    
    
    return 0;
}

void * otherMethord() {
    NSLog(@"线程2 ----------%@",[NSThread currentThread]);
    sleep(2);
    pthread_mutex_lock(&mutex_lock);
    NSLog(@"2号开始活动");
    pthread_mutex_unlock(&mutex_lock);
    
    return 0;
}

线程3 ----------<NSThread: 0x6000035bf5c0>{number = 4, name = (null)}
线程2 ----------<NSThread: 0x60000358cb00>{number = 5, name = (null)}
线程1 ----------<NSThread: 0x6000035bf380>{number = 3, name = (null)}
b------11
1号开始活动
2号开始活动

相关文章

  • 4.0.6.守护线程,线程死锁

    守护线程会随着主线程的结束而结束DaemonThread 线程 1, 线程 2,锁1,锁2 线程1 有锁1,想拿锁...

  • 悲观锁:一个线程得到锁,其它线程挂起,synchronized 乐观锁:一个线程得到锁,其它线程不断重试, cas...

  • sleep,wait, join yield

    锁池:所有需要竞争同步锁的线程都会放在锁池中,当一个线程得到锁后,其他线程都会在锁池中等待,当线程释放锁之后,其他...

  • 并发编程-线程

    线程 GIL 守护线程 线程锁(互斥锁 and 递归锁) 信号量 事件 条件 定时器 1.线程: 特点在多线程的操...

  • 深入理解AQS(二)- 共享模式

    共享锁与独占锁 独占锁被某个线程持有时,其他线程只能等待当前线程释放后才能去竞争锁,而且只有一个线程能竞争锁成功。...

  • iOS中各种锁的性能对比

    自旋锁 与 互斥锁 自旋锁 (spin lock): 如果一个线程需要获取自旋锁,该锁已经被其他线程占用,该线程不...

  • 死锁

    什么是死锁 简单的说:线程1持有A锁,线程2持有B锁;线程1尝试获取B锁,线程2尝试获取A锁。两个线程各持有了一把...

  • 公平锁和非公平锁-ReentrantLock是如何实现公平、非公

    1、什么是公平锁与非公平锁 公平锁:公平锁就是保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁。非公平锁:非...

  • 2020-03-11 公平锁与非公平锁

    补: 公平锁:多个线程情况下排队,先到先获得锁 非公平锁:当锁被释放后,所有线程竞争锁,抢到的线程就会获得锁 非公...

  • LINUX线程

    创建线程 启动线程 线程锁

网友评论

      本文标题:线程锁

      本文链接:https://www.haomeiwen.com/subject/stbwtctx.html