/**
卖票问题使用加锁,互斥锁
加锁,锁定的代码尽量少。
加锁范围内的代码, 同一时间只允许一个线程执行
互斥锁的参数:任何继承 NSObject *对象都可以。
要保证这个锁,所有的线程都能访问到, 而且是所有线程访问的是同一个锁对象
*/
-----------------------------------------------------------------
nonatomic 非原子属性
atomic 原子属性--默认属性
原子属性就是针对多线程设计的。 原子属性实现 单(线程)写 多(线程)读
"atomic(原子属性)在set方法内部加了一把自旋锁"
"nonatomic(非原子属性)下,set和get方法都不会加锁"
// 原子属性内部使用的 自旋锁
// 自旋锁和互斥锁
// 共同点: 都可以锁定一段代码。 同一时间, 只有线程能够执行这段锁定的代码
// 区别:互斥锁,在锁定的时候,其他线程会睡眠,等待条件满足,再唤醒
// 自旋锁,在锁定的时候, 其他的线程会做死循环,一直等待这条件满足,一旦条件满足,立马去执行,少了一个唤醒过程
// 如果同时重写了setter和getter方法,"_成员变量" 就不会提供
// 可以使用 @synthesize 合成指令,告诉编译器属性的成员变量的名称
@synthesize obj = _obj;
- (NSObject *)obj
{
return _obj;
}
// atomic情况下, 只要重写了set方法,getter也得重写
- (void)setObj:(NSObject *)obj
{
// 原子属性内部使用的 自旋锁
// 自旋锁和互斥锁
// 共同点: 都可以锁定一段代码。 同一时间, 只有线程能够执行这段锁定的代码
// 区别:互斥锁,在锁定的时候,其他线程会睡眠,等待条件满足,再唤醒
// 自旋锁,在锁定的时候, 其他的线程会做死循环,一直等待这条件满足,一旦条件满足,立马去执行,少了一个唤醒过程
@synchronized(self){ // 模拟锁。 真实情况下,使用的不是互斥锁。
_obj = obj;
}
}
-----------------------------------------------------------------
线程安全的概念: 就是在多个线程同时执行的时候,能够保证资源信息的准确性.
"UI线程" -- 主线程
** UIKit 中绝对部分的类,都不是”线程安全“的
"iOS里面是怎么解决这个线程不安全的问题?"
苹果约定,所有程序的更新UI都在主线程进行,也就不会出现多个线程同时改变一个资源。
// 在主线程更新UI,有什么好处?
1. 只在主线程更新UI,就不会出现多个线程同时改变 同一个UI控件
2. 主线程的优先级最高。也就意味UI的更新优先级高。 会让用户感觉很流畅
[NSThread sleepForTimeInterval:5.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
// 一旦强制终止,就在不能重新启动
// 一旦强制终止,后面的代码都不会执行
[NSThread exit];
// 不会写单词,怎么办?
//[[NSUserDefaults standardUserDefaults] synchronize];
// 为什么没有提示
// 因为苹果不推荐我们使用加锁。因为加锁,性能太差。
@synchronized(self){ // 开发的时候,一般就使用self就OK
面试题:
自动释放池,什么时候创建
1. 程序刚启动的时候,也会创建一个自动释放池
2.产生事件以后,运行循环开始处理事件,就会创建自动释放池
什么时候销毁的
1.程序运行结束之前销毁
2. 事件处理结束以后,会销毁自动释放池。
3. 还有在池子满的时候,也会销毁
子线程的自动释放池
默认是不工作的,就不会自动创建自动释放池 需要我们在子线程手动创建自动释放池
-在主线程中,向主队列添加同步任务,会造成死锁
-在其他线程中,向主队列向主队列添加同步任务,会在主线程中同步执行
具体是否会造成死锁,以及死锁的原因,还需要针对具体的情况分析,理解队列和执行任务的函数才是关键
建议:
-由于队列和执行任务的排列组合方式很多,不建议死记硬背
-前期不熟练的时候,不用紧张,只要掌握最常用的组合方式就可以了
-随着对 GCD 的使用增强,以及对队列和执行函数的理解加深,逐渐就好了。
同步任务的应用场景创建并发队列登录用同步,下载用异步
// 核心概念:
// 任务:block
// 队列:把任务放到队列里面,队列先进先出的原则,
// 串行队列:顺序,一个一个执行(必须一个任务执行完了,才能从队列里面取出下一个任务)
// 并发队列:同时,同时执行很多个任务(可以同时取出很多个任务,只要有线程去执行)
// 同步sync:不会开新线程
// 异步async:会开新线程,多线程的代名词
// 串行队列同步执行:不开线程,在原来线程里面一个一个顺序执行
// 串行队列异步执行:开一条线程,在这个新线程里面一个一个顺序执行
// 并发队列异步执行:开多个线程,并发执行(不一定是一个一个)执行
// 并发队列同步执行:不开线程,在原来线程里面一个一个顺序执行
// 阶段性总结:
// 1. 开不开线程,由执行任务方法决定,同步不开线程,异步肯定开线程
// 2. 开多少线程,由队列决定,串行 最多 开一个线程, 并发可以开多个线程。 具体开多少个,有GCD底层决定,程序猿不能控制
/**
队列的选择:
串行队列异步执行
- 开一条线程, 顺序执行
- 效率:不高,执行比较慢,资源占用小 -》 省电
一般网络是3G,对想能要求不会很高。
并发队列异步执行
- 开启多条线程,并发执行
- 效率:高,执行快,资源消耗大-》费电
使用场合:
- 网络WiFi,或者需要很快的响应,要求用户体验非常流畅。
-对任务执行顺序没有要求
-同步任务:一般只会在并发队列, 需要阻塞后续任务。必须等待同步任务执行完毕,再去执行其他任务。"依赖"关系
*/
/**
全局队列跟并发队列的区别
1. 全局队列没有名称 并发队列有名称
2. 全局队列,是供所有的应用程序共享。
3. 在MRC开发,并发队列,创建完了,需要释放。 全局队列不需要我们管理
*/
// 获得全局队列
/**
参数:第一个参数,一般 写 0(可以适配 iOS 7 & 8)
iOS 7
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
iOS 8
QOS_CLASS_DEFAULT 0
第二个参数:保留参数 0
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/**
主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
同步执行:要马上执行
结果:死锁
*/
- (void)once
{
static dispatch_once_t onceToken;
NSLog(@"%ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"%----ld", onceToken);
NSLog(@"真的执行一次么?");
});
NSLog(@"完成");
}
#pragma mark - 调度组(分组)
- (void)group
{
/**
应用场景:
开发的时候,有的时候出现多个网络请求都完成以后(每一个网络请求的事件长短不一定),再统一通知用户
比如: 下载小说:三国演义, 红楼梦, 金X梅
*/
// 实例化一个调度组
dispatch_group_t group = dispatch_group_create();
// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 任务添加到队列queue
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说A---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说B---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说X---%@", [NSThread currentThread]);
});
// 获得所有调度组里面的异步任务完成的通知
// dispatch_group_notify(group, queue, ^{
// NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
// });
//注意点: 在调度组完成通知里,可以跨队列通信
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 更新UI,在主线程
NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
});
}
#pragma mark - 延时操作
- (void)delay
{
/**
参数:
now 0
NSEC_PER_SEC: 很大的数字
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
// 参数: when : 表示从现在开始,经过多少纳秒以后
// queue : 在哪一个队列里面执行后面的任务
// dispatch_after(when, dispatch_get_main_queue(), ^{
// NSLog(@"点我了-- %@", [NSThread currentThread]);
// });
//
dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
NSLog(@"点我了-- %@", [NSThread currentThread]);
});
}