一、基本使用
1、三种创建子线程的方法
(1)NSThread直接创建,一个NSThread对象就代表一条线程
-
- NSThread *t1 = [[NSThreadalloc]initWithTarget:selfselector:@selector(longOperation:)object:@"NSThread"];
-
- [t1 start];
自定义一个耗时操作:
-
- - (void)longOperation:(id)obj {
- for (int i = 0; i < 10; ++i) {
- NSLog(@"%@ %d -- %@", [NSThreadcurrentThread], i, obj);
- }
- }
(2)、创建线程后自动启动线程
-
- [selfperformSelectorInBackground:@selector(longOperation:)withObject:@"perform"];
该方法不需要调用start方法
(3)隐式创建并启动线程
-
-
- NSObject *obj = [[NSObjectalloc]init]
- [obj performSelectorInBackground:@selector(loadData)withObject:nil];
2、主线程相关用法
- + (NSThread*)mainThread;
- - (BOOL)isMainThread;
- + (BOOL)isMainThread;
3、获得当前线程
- NSThread*current = [NSThreadcurrentThread];
修改主线程的栈区大小:
-
- NSThreadcurrentThread].stackSize = 11024 * 1024;
4、线程的调度优先级
- + (double)threadPriority;
- + (BOOL)setThreadPriority:(double)p;
- - (double)threadPriority;
- - (BOOL)setThreadPriority:(double)p;
优先级的取值范围为0.0-1.0,线程默认优先级是0.5,最高是1.0。
优先级高只能说明 CPU 在调度的时候,会优先调度,并不意味着优先级低的就不被调用或者后调用!
在多线程开发的时候,不要去做不同线程之间执行的比较!线程内部的方法都是各自独立执行的,如果设置了优先级,那么就会有可能出现低优先级的线程阻塞高优先级的线程,也就是优先级反转!在ios开发中,多线程最主要的目的就是把耗时操作放在后台执行
5、为线程设置名字
- - (void)setName:(NSString*)n;
- - (NSString*)name;
二、线程状态
每一个新建出来的线程被添加到可调度线程池中时就会处于就绪(runnable)状态,当CPU调度当前线程时,该线程会处于运行(running)状态,如果调用了sleep方法或者是等待同步锁时,该线程就会被移出可调度线程池,此时该线程处于阻塞(blocked)状态,当该线程sleep时间到时或得到同步锁又会被移入可调度线程池,此时该线程又处于就绪状态,当线程任务执行完毕或者异常强制退出时,该线程会处于死亡(dead)状态,如下图
启动线程:
此时线程从就绪状态到运行状态
阻塞线程
- + (void)sleepUntilDate:(NSDate*)date;
- + (void)sleepForTimeInterval:(NSTimeInterval)ti;
强制停止线程
此方法不会给任何机会去清理线程执行过程中分配的资源,也就是说一旦退出当前线程,后续所有代码不会执行(若后续代码中又释放内存的操作,那会相当的危险,会造成内存泄露),当使用C语言分配内存的时候要在该方法前适当的释放内存。
三、资源抢夺
多线程会有安全隐患。1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
如何解决隐患问题——使用互斥锁
假如有一个买票系统:
- - (void)saleTickets {
- while (YES) {
-
- [NSThreadsleepForTimeInterval:1.0];
- @synchronized(self) {
-
- if (self.tickets > 0) {
-
- self.tickets--;
-
-
- NSLog(@"剩余票数 %d %@", self.tickets, [NSThreadcurrentThread]);
- } else {
- NSLog(@"没票了 %@", [NSThreadcurrentThread]);
- break;
- }
- }
- }
- }
互斥锁的参数:
1、self 本质上是任意一个 NSObject 都可以当成锁!
2、 锁对象必须能够保证所有线程都能够访问(所以不可能是某个线程中的局部变量)
3、 如果在程序中,只有一个位置需要加锁,可以使用self对象
使用互斥锁的效果就是卖票正确了,但效率却是下降了,在ios开发中尽量不要区抢夺资源,也不要区使用同步锁
四、原子属性,互斥锁与自旋锁
(1)原子属性
nonatomic : 非原子属性
atomic :原子属性,是默认属性
* 是在多线程开发时,保证多个线程在"写入"的时候,能够保证只有一条线程执行写入操作!
* 是一个单(线程)写多(线程)读的多线程技术
* 原子属性,解决不了卖票问题,因为卖票的读写都需要锁定
* 有可能会出现"脏数据",重新读取一下就可以!
* 原子属性内部也有一把"锁"
* 原子属性的锁的性能要比互斥锁高!
(2)自旋锁与互斥锁
每一个原子属性里面都会有一个锁,称之为自旋锁
共同点:
都是保证同一时间,只有一条线程能够执行锁定范围的代码
区别:
互斥锁:如果发现代码已经被(其他线程)锁定,当前线程会进入休眠状态,等锁解除之后,重新被唤醒
自旋锁:如果发现代码已经被(其他线程)锁定,当前线程会以死循环的方式,一直判断锁是否解除,一旦接触立即执行!该锁,适合锁定非常短的代码,能够保证更高的执行性能!
互斥锁性能很差,在开发中很少用,只要是用到锁,性能都不高
(3) 线程安全
如果一个属性,在多个线程执行的情况下,仍然能够保证得到正确的结果,就叫做线程安全!要实现线程安全,就必须要使用到"锁"-> 性能不好!
iso开发中UI 线程有个约定,所有UI更新,都需要在主线程上执行!
原因:UIKit不是线程安全的!就是为了得到更高的性能!