- (void)longOperation:(id)obj {
NSLog(@"%@ - %@", [NSThread currentThread], obj);
}
方式1:alloc / init - start
- (void)threadDemo1 {
NSLog(@"before %@", [NSThread currentThread]);
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(longOperation:) object:@"THREAD"];
[thread start];
NSLog(@"after %@", [NSThread currentThread]);
}
代码小结
1 [thread start];执行后,会在另外一个线程执行 longOperation: 方法
2 在 OC 中,任何一个方法的代码都是从上向下顺序执行的
3 同一个方法内的代码,都是在相同线程执行的(block除外)
方式2:detachNewThreadSelector
- (void)threadDemo2 {
NSLog(@"before %@", [NSThread currentThread]);
[NSThread detachNewThreadSelector:@selector(longOperation:) toTarget:self withObject:@"DETACH"];
NSLog(@"after %@", [NSThread currentThread]);
}
代码小结
方式3:分类方法
- (void)threadDemo3 {
NSLog(@"before %@", [NSThread currentThread]);
[self performSelectorInBackground:@selector(longOperation:) withObject:@"PERFORM"];
NSLog(@"after %@", [NSThread currentThread]);
}
代码小结
? NSThread 的实例化方法中的 target 指的是开启线程后,在线程中执行 哪一个对象 的 @selector 方法
? 准备对象
? 定义属性
? 懒加载
? alloc / init
? detach
? 分类方法
? 通过指定不同的 target 会在后台线程执行该对象的 @selector 方法
? 提示:不要看见 target 就写 self
? performSelectorInBackground 可以让方便地在后台线程执行任意 NSObject 对象的方法
? 线程状态
? 新建
? 实例化线程对象
? 就绪
? 向线程对象发送 start 消息,线程对象被加入 可调度线程池 等待 CPU 调度
? detach 方法和 performSelectorInBackground 方法会直接实例化一个线程对象并加入 可调度线程池
? 运行
? CPU 负责调度可调度线程池中线程的执行
? 线程执行完成之前,状态可能会在就绪和运行之间来回切换
? 就绪和运行之间的状态变化由 CPU 负责,程序员不能干预
? 阻塞
? 当满足某个预定条件时,可以使用休眠或锁阻塞线程执行
? sleepForTimeInterval:休眠指定时长
? sleepUntilDate:休眠到指定日期
? @synchronized(self):乎斥锁
? 死亡
? 正常死亡
? 线程执行完毕
? 非正常死亡
? 当满足某个条件后,在线程内部中止执行
? 当满足某个条件后,在主线程中止线程对象
? 代码演练?- (void)statusDemo {
?
? NSLog(@"先睡会");
? [NSThread sleepForTimeInterval:1.0];
?
? for (int i = 0; i < 20; i++) {
? if (i == 9) {
? NSLog(@"再睡会");
? [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
? }
?
? NSLog(@"%d %@", i, [NSThread currentThread]);
?
? if (i == 16) {
? NSLog(@"88");
? // 终止线程之前,需要记住释放资源
? [NSThread exit];
? }
? }
? NSLog(@"over");
? }
? ?- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
? // 注意不要在主线程上调用 exit 方法
? // [NSThread exit];
?
? // 实例化线程对象(新建)
? NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(statusDemo) object:nil];
?
? // 线程就绪(被添加到可调度线程池中)
? [t start];
? }
? ?代码小结?阻塞
? 方法执行过程,符合某一条件时,可以利用 sleep 方法让线程进入 阻塞 状态
? sleepForTimeInterval 从现在起睡多少秒
? sleepUntilDate 从现在起睡到指定的日期
? 死亡?[NSThread exit];
?
? 一旦强行终止线程,后续的所有代码都不会被执行
? 注意1:在终止线程之前,应该注意释放之前分配的对象!
? 注意2:线程从就绪和运行状态之间的切换是由 CPU 负责的,程序员无法干预
? 线程属性?代码演练?// MARK: - 线程属性
? - (void)threadProperty {
? NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
?
? // 1. 线程名称
? t1.name = @"Thread AAA";
? // 2. 优先级
? t1.threadPriority = 0;
?
? [t1 start];
?
? NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
?
? // 1. 线程名称
? t2.name = @"Thread BBB";
? // 2. 优先级
? t2.threadPriority = 1;
?
? [t2 start];
? }
?
? - (void)demo {
? for (int i = 0; i < 10; ++i) {
? // 堆栈大小
? NSLog(@"%@ 堆栈大小:%tuK", [NSThread currentThread], [NSThread currentThread].stackSize / 1024);
? }
?
? // 模拟崩溃
? // 判断是否是主线程
? // if (![NSThread currentThread].isMainThread) {
? // NSMutableArray *a = [NSMutableArray array];
? //
? // [a addObject:nil];
? // }
? }
? ?属性?1. name - 线程名称
? 在大的商业项目中,通常需要在程序崩溃时,获取程序准确执行所在的线程
? 2. threadPriority - 线程优先级
? 优先级,是一个浮点数,取值范围从 0~1.0
? 1.0表示优先级最高
? 0.0表示优先级最低
? 默认优先级是0.5
? 优先级高只是保证 CPU 调度的可能性会高
? 刀哥个人建议,在开发的时候,不要修改优先级
? 多线程的目的:是将耗时的操作放在后台,不阻塞主线程和用户的交互!
? 多线程开发的原则:简单
? 3. stackSize - 栈区大小
? 默认情况下,无论是主线程还是子线程,栈区大小都是 512K
? 栈区大小可以设置
? [NSThread currentThread].stackSize = 1024 * 1024;
? ?4. isMainThread - 是否主线程
? 资源共享-卖票?多线程开发的复杂度相对较高,在开发时可以按照以下套路编写代码:
? 首先确保单个线程执行正确
? 添加线程
? 卖票逻辑?- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
? self.tickets = 20;
?
? [self saleTickets];
? }
?
? /// 卖票逻辑 - 每一个售票逻辑(窗口)应该把所有的票卖完
? - (void)saleTickets {
? while (YES) {
? if (self.tickets > 0) {
? self.tickets--;
? NSLog(@"剩余票数 %d %@", self.tickets, [NSThread currentThread]);
? } else {
? NSLog(@"没票了 %@", [NSThread currentThread]);
? break;
? }
? }
? }
? ?添加线程?- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
? self.tickets = 20;
?
? NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
? t1.name = @"售票员 A";
? [t1 start];
?
? NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
? t2.name = @"售票员 B";
? [t2 start];
? }
? ?添加休眠?- (void)saleTickets {
? while (YES) {
? // 模拟休眠
? [NSThread sleepForTimeInterval:1.0];
?
? if (self.tickets > 0) {
? self.tickets--;
? NSLog(@"剩余票数 %d %@", self.tickets, [NSThread currentThread]);
? } else {
? NSLog(@"没票了 %@", [NSThread currentThread]);
? break;
? }
? }
? }
? ?运行测试结果??互斥锁?添加互斥锁?- (void)saleTickets {
?
? while (YES) {
? [NSThread sleepForTimeInterval:1.0];
?
? @synchronized(self) {
? if (self.tickets > 0) {
? self.tickets--;
? NSLog(@"剩余票数 %d %@", self.tickets, [NSThread currentThread]);
? continue;
? }
? }
?
? NSLog(@"没票了 %@", [NSThread currentThread]);
? break;
? }
? }
? ?互斥锁小结
? 保证锁内的代码,同一时间,只有一条线程能够执行!
? 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差!
? 速记技巧 [[NSUserDefaults standardUserDefaults] synchronize];
? 互斥锁参数
? 能够加锁的任意 NSObject 对象
? 注意:锁对象一定要保证所有的线程都能够访问
? 如果代码中只有一个地方需要加锁,大多都使用 self,这样可以避免单独再创建一个锁对象
? 原子属性
? 原子属性(线程安全),是针对多线程设计的,是默认属性
? 多个线程在写入原子属性时(调用 setter 方法),能够保证同一时间只有一个线程执行写入操作
? 原子属性是一种单(线程)写多(线程)读的多线程技术
? 原子属性的效率比互斥锁高,不过可能会出现脏数据
? 在定义属性时,必须显示地指定 nonatomic
? 代码演练
? 定义属性
? @property (nonatomic, strong) NSObject *obj1;
? @property (atomic, strong) NSObject *obj2;
? @property (nonatomic, strong) NSObject *obj3;
?
? 模拟原子属性
? @synthesize obj3 = _obj3;
? - (void)setObj3:(NSObject *)obj3 {
? @synchronized(self) {
? _obj3 = obj3;
? }
? }
?
? - (NSObject *)obj3 {
? return _obj3;
? }
?
? * 性能测试
?
? - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
? int largeNumber = 1000 * 10000;
?
? NSLog(@"非原子属性");
? CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
? for (int i = 0; i < largeNumber; i++) {
? self.obj1 = [[NSObject alloc] init];
? }
? NSLog(@"%f", CFAbsoluteTimeGetCurrent() - start);
?
? NSLog(@"原子属性");
? start = CFAbsoluteTimeGetCurrent();
? for (int i = 0; i < largeNumber; i++) {
? self.obj2 = [[NSObject alloc] init];
? }
? NSLog(@"%f", CFAbsoluteTimeGetCurrent() - start);
?
? NSLog(@"模拟原子属性");
? start = CFAbsoluteTimeGetCurrent();
? for (int i = 0; i < largeNumber; i++) {
? self.obj3 = [[NSObject alloc] init];
? }
? NSLog(@"%f", CFAbsoluteTimeGetCurrent() - start);
? }
? ?原子属性内部的锁是自旋锁,自旋锁的执行效率比互斥锁高??自旋锁 & 互斥锁
? 共同点
? 都能够保证同一时间,只有一条线程执行锁定范围的代码
? 不同点
? 互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒
? 自旋锁:如果发现有其他线程正在执行锁定的代码,线程会以死循环的方式,一直等待锁定代码执行完成
? 结论
? 自旋锁更适合执行非常短的代码
? 无论什么锁,都是要付出代价
? 线程安全
? 多个线程进行读写操作时,仍然能够得到正确结果,被称为线程安全
? 要实现线程安全,必须要用到锁
? 为了得到更佳的用户体验,UIKit 不是线程安全的
? 约定:所有更新 UI 的操作都必须主线程上执行!?
? 因此,主线程又被称为UI 线程
? iOS 开发建议
? 所有属性都声明为 nonatomic
? 尽量避免多线程抢夺同一块资源
? 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"start");
// 同步执行
// [self demo];
// 异步执行
[self performSelectorInBackground:@selector(demo) withObject:nil];
NSLog(@"over");
}
- (void)demo {
NSLog(@"%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
NSLog(@"demo 完成");
}
代码小结
1 同步 从上到下顺序执行
异步 是多线程的代名词
原文:http://www.cnblogs.com/fakeCoder/p/5093652.html