二、多线程深入理解

1、线程间资源共享/抢夺 

(1)定义:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,当多个线程访问同一块资源时,各个线程提取和修改数据不同步,很容易引发数据错乱和数据安全问题。

 

(2)互斥锁(线程同步) :解决上面的问题

 

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

 

  •     · 每一个对象(NSObject)内部都有一个锁(变量),当有线程要进入synchronized到代码块中会先检查对象的锁是打开还是关闭状态,默认锁是打开状态(1),如果是线程执行到代码块内部 会先上锁(0)。如果锁被关闭,再有线程要执行代码块就先等待,直到锁打开才可以进入。

 

  •     · 互斥锁的实现流程

 

    线程执行到synchronized

i. 检查锁状态 如果是开锁状态转到ii  ,如果上锁转到v

ii. 上锁(0)

iii.        执行代码块

iv. 执行完毕 开锁(1)

v.        线程等待(就绪状态)

 

 

2、原子属性

 

(1)属性中的修饰符

 

    • nonatomic :非原子属性
    • atomic  : 原子属性,针对多线程设计的,默认值。保证同一时间只有一个线程能够写入,但是同一个时间多个线程都可以取值。
    • (2)自旋锁:atomic  本身就有一把锁(自旋锁),保证“单写多读”:单个线程写入,多个线程可以读取。如果发现有其它线程正在锁定代码,线程会用死循环的方式,一直等待锁定的代码执行完成 。自旋锁更适合执行不耗时的代码。
  • (3)iOS开发的建议
    • 所有属性都声明为nonatomic,移动设备内存小,atomic虽然相对线程安全,但是消耗资源较多。
    • 尽量避免多线程抢夺同一块资源。
    • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。

 

 

 

 

3、线程安全

 

    • (1)多个线程同时操作一个全局变量是不安全的,使用自旋锁并不是绝对的安全(因为单写多读)。
    • (2)线程安全:在多个线程进行读写操作时,仍然能够保证数据的正确 。使用互斥锁可以实现,但是消耗性能。
  • (3)关于主线程(UI线程):几乎所有UIKit􏰀提供的类都是线程不安全的,所有更新UI的操作都在主线程上执行。

 

 

4、NSRunLoop

 

(1)功能作用:运行循环,又叫消息循环或事件循环。

 

  • 检测、接受“输入事件”并执行。

 

  • 保证程序不退出(主线程)。

 

  • 如果没有事件发生,会让程序进入休眠状态

 

(2)特点:

 

  • NSRunLoop不能单独存在,必须存在于线程中,不论主线程中还是子线程中都有一个消息循环。注意:主线程的RunLoop是默认开启的,子线程中的RunLoop默认不开启。

 

  • 只要线程一启动,内部就会有一个默认的主RunLoop,而每一个App,只要一启动,就会自动有一个主线程。

 

 

 

(3)(两大核心之一)输入事件:输入源(比如键盘输入,滚动scrollView,performSelector方法等等),定时源(定时器)

 

 

(4)(两大核心之二)运行模式(消息循环模式):

 

  • 线程的消息循环运行在某一种消息循环模式上。

 

  • 输入事件必须设置消息循环的运行模式,并且如果想让输入事件可以在消息循环上执行,输入事件的消息循环运行模式必须和当前消息循环的运行模式一致

 

  • 两种常用的运行模式:NSDefaultRunLoopMode、NSRunLoopCommonModes。

 

  • 例子:输入事件是定时源

 

    //定时源 (计时器)

    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];

    

    /*

        参数1:输入源

        参数2:输入源的模式,要和当前消息循环的模式对应,才可以让消息循环执行输入源

        NSDefaultRunLoopMode默认模式 NSRunLoopCommonModes包含了很多种模式

    */

    

    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

 

 

5、自动释放池

 

  • (1)主线程自动释放池的创建和销毁:
  • 每一次主线程的消息循环开始的时候会先创建自动释放池。

 

  • 消息循环结束前,会释放自动释放池。

 

  • 自动释放池被销毁或耗尽时会向池中所有对象发送 release 消息,释放所有 autorelease 的对象。

 

  • 自动释放池随着消息循环的开始和结束不断的重建和销毁。

 

(2)子线程的自动释放池:

 

  •     在子线程开启时手动创建释放池。因为主线程可以自动生成释放池,而子线程不可以。为了保证消息循环结束(线程结束)时,所有的对象可以正常入池和释放,必须手动添加。

 

  •       ·     其他情况和主线程相同。

 

  • (3)什么时候使用自动释放池:(官方文档建议)
  •  
  •          ·     开启子线程时。

 

         

         例如:

 

for (int i = 0; i < largeNumber; ++i) {

@autoreleasepool {

 

NSString *str = @"Hello World";

    str = [str stringByAppendingFormat:@" - %d", i];

    str = [str uppercaseString];

 

}

}

 

(4)示意图

 

自动释放池.png

 

posted @ 2016-03-11 01:05  独孤求傲  阅读(312)  评论(0编辑  收藏  举报