iOS中的自旋锁与互斥锁

作者: 多喝烫水_KK | 来源:发表于2018-06-09 23:26 被阅读56次
一:自旋锁与互斥锁

自旋锁:是一种用于保护多线程共享资源的锁,与一般互斥锁(mutex)不同之处在于当自旋锁尝试获取锁时以忙等待(busy waiting)的形式不断地循环检查锁是否可用。当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会一直等待(不会睡眠),当上一个线程的任务执行完毕,下一个线程会立即执行。
在多CPU的环境中,对持有锁较短的程序来说,使用自旋锁代替一般的互斥锁往往能够提高程序的性能。

互斥锁:当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会进入睡眠状态等待任务执行完毕,当上一个线程的任务执行完毕,下一个线程会自动唤醒然后执行任务。

自旋锁与互斥锁的相同点和不同点:

相同点:都能保证同一时间只有一个线程访问共享资源。都能保证线程安全。
不同点
自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。
互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。
结论:自旋锁的效率高于互斥锁。

优缺点分析:

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

自旋锁应用场景:
比较适合做一些不耗时的操作

二:多线程安全隐患

资源共享
1块资源可呢会被多个线程共享,也就是多个线程可能会访问到一块资源,
比如多个线程访问同一个对象,同一个变量,同一个文件。当多线程访问同一块资源的时候,很容易引发数据错乱和数据安全问题。

注意点:
如果多线程访问同一个资源,那么必须使用同一把锁才能锁住。在开发中,尽量不要加锁,能在服务端做尽量在服务端做,如果必须要加锁,一定要记住,锁的范围不能太大,哪里有安全隐患就加在哪里。

synchronized(互斥锁) 与 atomic(自旋锁):
  • 原子和非原子属性(nonatomic与atomic)

OC 在定义属性的时候有nonatomic和atomic等两种选择
atomic:原子属性,为 setter 方法加锁;
nonatomic:非原子属性,不会为 setter 方法加锁;
普通情况下都是在主线程做操作,所以一般都不会加锁。

对比:
共同点:都能保证同一时刻只能有一个线程操作锁住的代码;
不同点:
atomic:线程安全,需要消耗大量的资源,是一种单(线程)写多(线程)读的多线程技术;
nonatomic:非线程安全,适合内存小的移动设备;

  • synchronized 条件锁

优点:我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,能有效防止因多线程抢夺资源造成的数据安全问题;
缺点:会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁,需要消耗大量的CPU资源;
使用最简单,性能也最差。

@synchronized(obj) { 
// 内部会添加异常处理,所以耗时 
NSLog(@"自动加锁,自动解锁,自动销毁"); 
} 

obj为该锁的唯一标识,只有当标识相同时,才为满足互斥。
因为必须使用同一把锁,开发中如果需要加锁,可直接使用 self 。

@synchronized(self) {
      //线程1进来之后,锁住,2和3都再外面等待
      //1、查询剩余的票数
      NSUInteger count = self.totalCount;
      //2、判断是否还有余票
      //2.1卖票
      //3 、提示客户,没有票了
      if (count>0) {
          [NSThread sleepForTimeInterval:0.1];
          self.totalCount = count-1;
          NSLog(@"%@卖了一张票,还剩%zd票",[NSThread currentThread].name,self.totalCount);
      }else{
          NSLog(@"没票了");
          break;
      }
  }//解锁
三:提问:

1. synchronized锁住的是代码还是对象?

synchronized锁住的是括号里的对象,而不是代码。
当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。synchronized中传入的object的内存地址,被用作key,通过hash map对应的一个系统维护的递归锁。
所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。

2.atomic真的能保证对象的线程安全?

atomic 不能保证对象多线程的安全。它只是能保证你访问的时候给你返回一个完好无损的Value而已。
举个例子:如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,有3种可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。所以atomic可并不能保证对象的线程安全。

本文参考文章:
互斥锁和自旋锁的对比
iOS atomic 对象是线程不安全的原因以及与 nonatomic 的区别

相关阅读:
不再安全的 OSSpinLock
atomic 和 nonatomic 有什么区别?
为什么说在持有自旋锁时不能进入睡眠或阻塞

相关文章

  • iOS面试题与核心基础之线程同步(锁,串行队列,信号量,@syn

    锁 iOS多线程锁有两类 自旋锁 和 互斥锁自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。资源已...

  • iOS 多线程面试题(自旋锁与互斥锁)

    自旋锁与互斥锁 自旋锁: 是一种用于保护多线程共享资源的锁,与一般互斥锁(mutex)不同之处在于当自旋锁尝试获取...

  • iOS中的自旋锁与互斥锁

    一:自旋锁与互斥锁 自旋锁:是一种用于保护多线程共享资源的锁,与一般互斥锁(mutex)不同之处在于当自旋锁尝试获...

  • iOS 开发中加锁

    [1].OSSpinLock 自旋锁 [1]自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被...

  • 线程锁

    1.常见的锁包括:互斥锁,自旋锁。 2.互斥锁是指锁的类型,自旋锁是指锁的实现方式。 3.互斥锁:当上...

  • iOS底层探索-多线程锁

    多线程的锁大致可分为两大类:互斥锁、自旋锁;也可以分为三类:互斥锁、自旋锁、读写锁。 一、互斥锁:互斥+同步(强调...

  • ios 锁的种类及性能

    一、基本概念 ios中的锁主要可以分为两大类,互斥锁 和 自旋锁,其他锁都是这两种锁的延伸和扩展。 1、介绍 互斥...

  • iOS 锁

    同步锁 自旋锁 互斥锁

  • 自旋锁与互斥锁

    自旋锁(Spin lock) 自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保...

  • CLH并发队列

    1 什么是自旋锁和互斥锁? 由于CLH锁是一种自旋锁,那么我们先来看看自旋锁是什么? 自旋锁说白了也是一种互斥锁,...

网友评论

  • 61cbb200688a:那么什么可以完全保证对象的线程安全
    多喝烫水_KK:@港吧休 操作对象的时候加锁就好了(我不怎么看消息..

本文标题:iOS中的自旋锁与互斥锁

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