线程安全

来源:互联网 发布:知乎 埃斯顿 伺服电机 编辑:程序博客网 时间:2024/05/29 03:15

iOS开发多线程篇—线程安全

 

一、多线程的安全隐患

资源共享

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

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

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

示例一:

示例二:

问题代码:

复制代码
 1 // 2 //  YYViewController.m 3 //  05-线程安全 4 // 5 //  Created by apple on 14-6-23. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 10 #import "YYViewController.h"11 12 @interface YYViewController ()13 //剩余票数14 15 @property(nonatomic,assign) int leftTicketsCount;16 @property(nonatomic,strong)NSThread *thread1;17 @property(nonatomic,strong)NSThread *thread2;18 @property(nonatomic,strong)NSThread *thread3;19 20 21 @end22 23 24 @implementation YYViewController25 26 27 - (void)viewDidLoad28 {29     [super viewDidLoad];30 31     //默认有20张票32 33     self.leftTicketsCount=10;34 35     //开启多个线程,模拟售票员售票36 37     self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];38 39     self.thread1.name=@"售票员A";40 41     self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];42 43     self.thread2.name=@"售票员B";44 45     self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];46     self.thread3.name=@"售票员C";47 }48 49  50 -(void)sellTickets51 {52     while (1) {53         //1.先检查票数54         int count=self.leftTicketsCount;55         if (count>0) {56             //暂停一段时间57             [NSThread sleepForTimeInterval:0.002];58 59             //2.票数-160            self.leftTicketsCount= count-1;61  62             //获取当前线程63             NSThread *current=[NSThread currentThread];64             NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);65         }else66         {67             //退出线程68             [NSThread exit];69         }70     }71 }72 73 74 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event75 { 76     //开启线程77 78    [self.thread1 start];79     [self.thread2 start];80     [self.thread3 start];81 82 }83 84 @end
复制代码

 

打印结果:

 

二、安全隐患分析

 

 

三、如何解决

 

互斥锁使用格式

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

注意:锁定1份代码只用1把锁,用多把锁是无效的

 

代码示例:

复制代码
 1 // 2 //  YYViewController.m 3 //  05-线程安全 4 // 5 //  Created by apple on 14-6-23. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYViewController.h"10 11 @interface YYViewController ()12 13 //剩余票数14 @property(nonatomic,assign) int leftTicketsCount;15 @property(nonatomic,strong)NSThread *thread1;16 @property(nonatomic,strong)NSThread *thread2;17 @property(nonatomic,strong)NSThread *thread3;18 @end19 20 @implementation YYViewController21 22 - (void)viewDidLoad23 {24     [super viewDidLoad];25     //默认有20张票26     self.leftTicketsCount=10;27     //开启多个线程,模拟售票员售票28 29     self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];30 31     self.thread1.name=@"售票员A";32 33     self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];34 35     self.thread2.name=@"售票员B";36 37     self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];38 39     self.thread3.name=@"售票员C";40 }41 42 43 -(void)sellTickets44 {45     while (1) {46         @synchronized(self){//只能加一把锁47         //1.先检查票数48 49         int count=self.leftTicketsCount;50         if (count>0) {51             //暂停一段时间52             [NSThread sleepForTimeInterval:0.002];53             //2.票数-154 55            self.leftTicketsCount= count-1;56             //获取当前线程57             NSThread *current=[NSThread currentThread];58             NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);59 60         }else61         {62             //退出线程63             [NSThread exit];64         }65         }66     }67 }68 69 70 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event71 {72 73     //开启线程74    [self.thread1 start];75     [self.thread2 start];76     [self.thread3 start];77 }78 79 @end
复制代码

执行效果图

 

互斥锁的优缺点

优点:能有效防止因多线程抢夺资源造成的数据安全问题

缺点:需要消耗大量的CPU资源

 

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

相关专业术语:线程同步,多条线程按顺序地执行任务

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

 

四:原子和非原子属性

 

OC在定义属性时有nonatomicatomic两种选择

atomic:原子属性,为setter方法加锁(默认就是atomic

nonatomic:非原子属性,不会为setter方法加锁

 

atomic加锁原理

复制代码
1 @property (assign, atomic) int age;2 3 - (void)setAge:(int)age4 { 5 6     @synchronized(self) { 7        _age = age;8     }9 }
复制代码

 

原子和非原子属性的选择

nonatomic和atomic对比

atomic线程安全,需要消耗大量的资源

nonatomic非线程安全,适合内存小的移动设备

 

iOS开发的建议

所有属性都声明为nonatomic

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

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

0 0