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 @end 22 23 24 @implementation YYViewController
25 26 27 - (void)viewDidLoad
28 {
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)sellTickets
51 {
52 while (1) {
53 //1.先检查票数 54 int count=self.leftTicketsCount;
55 if (count>0) {
56 //暂停一段时间 57 [NSThread sleepForTimeInterval:0.002];
58 59 //2.票数-1 60 self.leftTicketsCount= count-1;
61 62 //获取当前线程 63 NSThread *current=[NSThread currentThread];
64 NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
65 }else 66  {
67 //退出线程 68  [NSThread exit];
69  }
70  }
71 }
72 73 74 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 75 {
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 @end 19 20 @implementation YYViewController
21 22 - (void)viewDidLoad
23 {
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)sellTickets
44 {
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.票数-1 54 55 self.leftTicketsCount= count-1;
56 //获取当前线程 57 NSThread *current=[NSThread currentThread];
58 NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
59 60 }else 61  {
62 //退出线程 63  [NSThread exit];
64  }
65  }
66  }
67 }
68 69 70 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 71 {
72 73 //开启线程 74  [self.thread1 start];
75  [self.thread2 start];
76  [self.thread3 start];
77 }
78 79 @end


执行效果图


互斥锁的优缺点

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

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

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

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

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

四:原子和非原子属性

OC在定义属性时有nonatomic和atomic两种选择

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

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

atomic加锁原理

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


原子和非原子属性的选择

nonatomic和atomic对比

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

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

iOS开发的建议

所有属性都声明为nonatomic

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

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

时间: 2024-11-10 00:37:04

iOS开发多线程篇—线程安全的相关文章

iOS开发多线程篇—线程的状态

一.简单介绍 线程的创建: self.thread=[[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil]; 说明:创建线程有多种方式,这里不做过多的介绍. 线程的开启: [self.thread start]; 线程的运行和阻塞: (1)设置线程阻塞1,阻塞2秒 [NSThread sleepForTimeInterval:2.0]; (2)第二种设置线程阻塞2,以当前时间为基准阻塞4秒 NSDate

iOS开发多线程篇—线程间的通信

一.简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任务后,转到另1个线程继续执行任务 线程间通信常用方法 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelector:(SEL)aSelector onThr

iOS开发多线程篇—多线程简单介绍

一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过"活动监视器"可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行 3.线程的串行 1个线程中任务的执

iOS开发多线程篇—创建线程

一.创建和启动线程简单说明 一个NSThread对象就代表一条线程 创建.启动线程 (1) NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start]; // 线程一启动,就会在线程thread中执行self的run方法 主线程相关用法 + (NSThread *)mainThread; // 获得主线程 - (BOOL)isMainThr

iOS开发多线程篇—GCD介绍

一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为"牛逼的中枢调度器" 纯C语言,提供了非常多强大的函数 2.GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自动利用更多的CPU内核(比如双核.四核) GCD会自动管理线程的生命周期(创建线程.调度任务.销毁线程) 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码 3.提示 (1)GCD存在于libdispatch.dylib这个库中,这个调度库包含了GC

iOS开发多线程篇—NSOperation简单介绍

一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现多线程编程 NSOperation和NSOperationQueue实现多线程的具体步骤: (1)先将需要执行的操作封装到一个NSOperation对象中 (2)然后将NSOperation对象添加到NSOperationQueue中 (3)系统会⾃动将NSOperationQueue中的NSOperation取出来 (4)将取出的NSOperati

iOS开发多线程篇—NSOperation基本操作

一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. (3)最⼤大并发数的相关⽅方法 - (NSInteger)maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 说明:如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点. 注意:num

iOS开发多线程篇—GCD的常见用法

一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) withObject:nil afterDelay:2.0]; // 2秒后再调用self的run方法 (2)使用GCD函数 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue()

iOS开发多线程篇—GCD的基本使用

一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程. 获取主队列的方式: dispatch_queue_t queue=dispatch_get_main_queue(); (1)使用异步函数执行主队列中得任务,代码示例: 1 // 2 // YYViewController.m 3 // 12-GCD的基本使用(主队列) 4