多线程

/**

 卖票问题使用加锁,互斥锁

 加锁,锁定的代码尽量少。

 加锁范围内的代码, 同一时间只允许一个线程执行

 互斥锁的参数:任何继承 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]);

    });

}

你可能感兴趣的:(多线程)