4.3 NSThread->4.0 多线程安全隐患

本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正。


本文相关目录:
==================== 所属文集:4.0 多线程 ====================
4.1 多线程基础->1.0 进程 & 线程
······················ 2.0 多线程简介
4.2 pthread
4.3 NSThread->1.0 创建线程
····················· 2.0 线程属性
····················· 3.0 线程状态/线程生命周期
····················· 4.0 多线程安全隐患
····················· 5.0 线程间通讯和常用方法
4.4 GCD->1.0 GCD简介和使用
·············· 2.0 线程间的通信
·············· 3.0 其他用法
·············· 4.0 GCD 的定时器事件
4.5 NSOperation->1.0 NSOperation简介
························ 2.0 NSOperationQueue
························ 3.0 线程间通信
························ 4.0 自定义NSOperation
4.6 RunLoop - 运行循环
===================== 所属文集:4.0 多线程 =====================


4.1 资源共享/抢夺

多线程的安全隐患:

(1) 起因 :

资源共享概念 : 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源

主要是因为多条线程,对`同一资源同时操作`,导致的问题

(2) 举例 :

比如多个线程访问同一个对象、同一个变量、同一个文件

(3) 结果 :

当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题 (资源强夺)

(4) 解决方案 :

互斥锁 / 同步锁

(5) 实例1 :卖票案例

多线程开发的复杂度相对较高,在开发时可以按照以下套路编写代码:

- 首先确保单个线程执行正确
- 添加线程

代码示例 :

#import "ViewController.h"

@interface ViewController ()
@property(nonatomic, strong) NSThread *thread01; // 售票员01
@property(nonatomic, strong) NSThread *thread02; // 售票员02
@property(nonatomic, strong) NSThread *thread03; // 售票员03

@property(nonatomic, assign) NSInteger ticketCount; //票的总数
@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.

  self.ticketCount = 10;

  //创建线程
  self.thread01 = [[NSThread alloc] initWithTarget:self
                                          selector:@selector(saleTicket)
                                            object:nil];
  self.thread01.name = @"售票员01";

  self.thread02 = [[NSThread alloc] initWithTarget:self
                                          selector:@selector(saleTicket)
                                            object:nil];
  self.thread02.name = @"售票员02";

  self.thread03 = [[NSThread alloc] initWithTarget:self
                                          selector:@selector(saleTicket)
                                            object:nil];
  self.thread03.name = @"售票员03";
}

// 开启线程
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  [self.thread01 start];
  [self.thread02 start];
  [self.thread03 start];
}

// 卖票
- (void)saleTicket {

  while (1) {

    @synchronized(self) { //互斥锁(控制器做锁对象)
      // 先取出总数
      NSInteger count = self.ticketCount;

      // 判断还有没有余票
      if (count > 0) {
        self.ticketCount = count - 1;
        NSLog(@"%@卖了一张票,还剩下%zd张", [NSThread currentThread].name,
              self.ticketCount);
      } else {
        NSLog(@"票已经卖完了");
        break;
      }
    }
  }
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

@end

打印结果:

线程安全[3386:472835]售票员02卖了一张票,还剩下9张
线程安全[3386:472836]售票员03卖了一张票,还剩下8张
线程安全[3386:472834]售票员01卖了一张票,还剩下7张
线程安全[3386:472835]售票员02卖了一张票,还剩下6张
线程安全[3386:472836]售票员03卖了一张票,还剩下5张
线程安全[3386:472834]售票员01卖了一张票,还剩下4张
线程安全[3386:472835]售票员02卖了一张票,还剩下3张
线程安全[3386:472836]售票员03卖了一张票,还剩下2张
线程安全[3386:472834]售票员01卖了一张票,还剩下1张
线程安全[3386:472835]售票员02卖了一张票,还剩下0张
线程安全[3386:472836]票已经卖完了
线程安全[3386:472834]票已经卖完了
线程安全[3386:472835]票已经卖完了

4.2 安全隐患解决 – 互斥锁 / 同步锁

互斥锁使用技术 : 线程同步
概念:多条线程按顺序地执行任务

引申 : 互斥锁,就是使用了线程同步技术

互斥锁使用格式 :
//锁对象为能够加锁的任意 NSObject 对象
//锁对象一定要保证所有的线程都能够访问
//如果代码中只有一个地方需要加锁,大多都使用self,这样可以避免单独再创建一个锁对象

@synchronized(锁对象) {
    //需要锁定的代码
}

注意:

- 锁定1份代码只用1把锁,用多把锁是无效的

- 保证锁内的代码,同一时间,只有一条线程能够执行!

- 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差!

- 速记 :  [[NSUserDefaults standardUserDefaults] synchronize];

互斥锁的使用前提:
多条线程抢夺同一块资源

互斥锁的优缺点 :
优点:能有效防止因多线程抢夺资源造成的数据安全问题 , 能保证数据的准确性

缺点:需要消耗大量的CPU资源 , 可能会导致执行速度变慢

4.3 原子属性/非原子属性

原子和非原子属性:
(默认) atomic 原子属性 为setter方法加锁 线程安全,需要消耗大量的资源
(推荐) nonatomic 非原子属性 不会为setter方法加锁 非线程安全,适合内存小的移动设备

atomic加锁原理:
@property (assign, atomic) int age;
- (void)setAge:(int)age
{
    @synchronized(self) {
        _age = age;
    }
}

iOS开发的建议:
- 所有属性都声明为nonatomic

- 尽量避免多线程抢夺同一块资源

- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

本文源码 Demo 详见 Github
https://github.com/shorfng/iOS_4.0_multithreading.git





作者:蓝田(Loto)
出处:

如果你觉得本篇文章对你有所帮助,请点击文章末尾下方“喜欢”
如有疑问,请通过以下方式交流:
评论区回复微信(加好友请注明“+称呼”)发送邮件[email protected]



本文版权归作者和本网站共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

你可能感兴趣的:(4.3 NSThread->4.0 多线程安全隐患)