iOS开发多线程--线程安全

多线程的安全隐患

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

@interface ViewController ()
 //剩余票数
@property(nonatomic,assign) int leftTicketsCount;
@property(nonatomic,strong)NSThread *thread1;
 @property(nonatomic,strong)NSThread *thread2;
 @property(nonatomic,strong)NSThread *thread3;
 @end
 @implementation ViewController
 - (void)viewDidLoad
 {
  [super viewDidLoad];
    //默认有20张票
    self.leftTicketsCount=10;
    //开启多个线程,模拟售票员售票
    self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
    self.thread1.name=@"售票员A";
    self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
    self.thread2.name=@"售票员B";
    self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
    self.thread3.name=@"售票员C";
}
- (void)sellTickets
 {
 while (1) {
 //1.先检查票数
 int count=self.leftTicketsCount;
 if (count>0) {
     //暂停一段时间
  [NSThread sleepForTimeInterval:0.002];
  //2.票数-1
  self.leftTicketsCount= count-1;
   //获取当前线程
  NSThread *current=[NSThread currentThread];
 NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
 }else
   {
 //退出线程
          [NSThread exit];
      }
   }
 }
 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 { 
  //开启线程
 [self.thread1 start];
   [self.thread2 start];
    [self.thread3 start];
}
 @end

结果是数据错乱:


1.png
  • 安全隐患分析:
2.png
3.png
  • 解决办法,你需要加一把“锁”,看代码吧:
// 将上边的错误代码中的这个方法加以修改就好了就是加了一个@synchronized(self)
 -  (void)sellTickets
{
    while (1) {
     @synchronized(self){//只能加一把锁

        //1.先检查票数
        int count=self.leftTicketsCount;
        if (count>0) {
            //暂停一段时间
            [NSThread sleepForTimeInterval:0.002];
            //2.票数-1
            self.leftTicketsCount= count-1;
            //获取当前线程
            NSThread *current=[NSThread currentThread];
            NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
        }else
        {
            //退出线程
            [NSThread exit];
        }
    }
    }
}

其他代码无需更改,再试下是不是就好了!保证每张票都是只被卖出一次。这个“锁”就是保证数据不发生错乱的根本保障。

  • 互斥锁的优缺点
    优点:能有效防止因多线程抢夺资源造成的数据安全问题
    缺点:需要消耗大量的CPU资源
  • 互斥锁的使用前提:多条线程抢夺同一块资源
    相关专业术语:线程同步,多条线程按顺序地执行任务
    互斥锁,就是使用了线程同步技术

原子和非原子属性

  • OC在定义属性时有nonatomic和atomic两种选择
    atomic:原子属性,为setter方法加锁(默认就是atomic)
    nonatomic:非原子属性,不会为setter方法加锁
    atomic加锁原理
 @property (assign, atomic) int age;
 - (void)setAge:(int)age
 { 
  @synchronized(self) { 
    _age = age;
 }
 }
  • 原子和非原子属性的选择
    nonatomic和atomic对比
    atomic:线程安全,需要消耗大量的资源
    nonatomic:非线程安全,适合内存小的移动设备

  • iOS开发的建议
    所有属性都声明为nonatomic
    尽量避免多线程抢夺同一块资源
    尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容