1、多线程的状态:
当线程创建时,只是在内存中创建了;只有在线程进入 就绪状态 的时候,线程才会加入 可调度线程池 ;这样CPU便可以调用线程;
2、多线程使用的几种方法:
(1)pthread 可移植多线程使用:
//创建pthread
pthread_tPID;
NSString*str =@"str";
//char *str = "char * ssss";
//参数1:pthread_t线程的标示
//参数2:pthread_attr_t线程的属性
//参数3:void * (*) (void *)函数签名
// 返回值 函数名 参数类型
//void *相当于oc中的id类型,可以指向任意;
// //参数4:给函数指定的参数
//__bridge标示非oc对象也需要被内存管理释放,在ARC机制下,编译器不对基础变量做内存管理;
int result =pthread_create(&PID,NULL,task2, (__bridge void*)(str));
(2)NSThread方法:创建的线程有.name属性,可以给线程一个唯一的名字,方便自己调试;
优先级属性:
.
threadPriority;赋值的范围是0.0-1.0。1.0是最高的优先级,默认的是0.5;但是优先级高并不意味着会被先执行;
在子线程需要主线程更新UI界面时,需要调用,方法就会在主线程执行;
[self performSelectorOnMainThread:@selector(updateUI:)withObject:image waitUntilDone:NO];
NSThread的创建的三种方法:
1、需要手动开启线程
NSThread*thread = [[NSThread alloc]initWithTarget:self selector:@selector(task)object:nil];
//开启线程 [threadstart];
2、自动开启线程,一创建就可以被CPU调用;
[NSThread detachNewThreadSelector:@selector(task)toTarget:selfwithObject:nil];
3、影式的创建线程
[self performSelectorInBackground:@selector(task)withObject:nil];
(3)GCD的创建方法:
dispatch_async 和 dispatch_sync创建同步或者异步的多线程;参数一:是一个消息队列;参数二:是需要执行的任务,是一个无参数无返回值的Block;
在GCD中,如果需要做UI跟新,或者需要回到主线程;只是需要获取 主队列 (主队列是一个串行的队列,会用不执行);将任务添加进去就可以了:
dispatch_async(dispatch_get_main_queue(), ^{
code;
});
在GCD有很多个任务的时候,并不是有多少个任务就会开多少的子线程,子线程上的任务执行完成后并不会马上销毁,会被重新使用,所以用GCD创建多线程异步执行的时候并不知道有多少子线程被创建;
(4)NSOperation方法:这是一个抽象类,不能实例化;此方法创建多线程时,都是通过子类,或者自定义子类实现(需要实现对应的方法);这个类其实是对GCD的一个封装,但其实NSOperation出现的时间比GCD 早;
NSOperation创建多线程时,首先需要一个 操作队列 ,即NSOperationQueue。
NSOperationQueue里面有两个属性是特有的:
suspended、 maxConcurrentOperationCount;前者是将队列挂起操作(即暂停操作队列的操作,但是对当前正在执行的操作不受影响);后者是对最大并发数的限制,即在同一时刻最多能有几个操作在执行;
还有一个方法 cancelAllOperations;这是取消队列中剩下的操作,会将队列清空;
创建操作队列后,可以直接使用Block添加操作至队列:
[
self
.opQueue addOperationWithBlock:^{
<#所需要执行的操作#>
}]
也可以先创建NSOperation的子类
NSInvocationOperation、NSBlockOperation的一个操作;然后添加至创建的操作队列,如果不添加操作队列,NSOperation有一个start属性,这样就不会开启子线程,会在当前线程执行(相当就是在主线程);
依赖关系:操作之间可以设置执行的顺序,是通过依赖关系;而且可以 跨队列 设置依赖关系;比如:op1操作一栏op2,op2依赖于op3;它们的执行顺序就是:op3、op2、op1;
[op1 addDependency:op2];
[op2 addDependency:op3];
与主线程的通信:就是获取main队列,然后添加操作:
[[
NSOperationQueue
mainQueue
]
addOperation
:op3];
注意事项:
1、NSOperation创建的操作队列,是异步多线程执行的,相当于GCD中的并发队列;它没有同步执行;
2、一般在使用的时候,都会创建一个全局的操作队列,方便于管理;
3、多线程之间的数据共享(同步锁和自旋锁)
当多个线程同时访问数据并且更该时,很容易发生错误;这时就需要给数据加上锁,在同一时间只能有一个线程访问;
@synchronized(self.tickets){
if(self.tickets>0){
self.tickets=self.tickets- 1;
NSLog(@"%@余票%d",[NSThreadcurrentThread],self.tickets);
continue;
}
}
@synchronized是同步的意思;线程必须要按顺序执行;这是同步锁;
自旋锁:是系统帮我们添加的。在我们创建属性的时候有nonatomic和atomic;前者是非原子属性,后者是原子属性,它的内部有一把自旋锁;但是锁的只是setter方法;并没有对getter方法上锁;这可能在判断的时候会出错;
自旋锁会一直判断能不能访问;相当于一个死循环;会占用内存,所以一般不推荐使用自旋锁;
4、消息分为两种:输入源和定时源;
当消息需要加入消息循环时,需要设置对应的Modle,否则会没有作用;
主线程的消息循环值默认开启的,子线的时关闭的,需要手动开启;
子线程开启消息循环有三种方法:
1、run(开启便不能停止);
2、runUntilDate运行到一个时间点(局限性,每个手机的性能不一样);
3、苹果官方推荐的方法:通过BOOL变量控制;
NSDate是返回一个比较遥远的未来时间;
BOOL shouldKeepRunning = YES;
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning&& [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
5、GCD队列:串行队列、并发队列、主队列(一个特殊串行队列);
串行队列,同步执行,不开新的线程,在当前线程下执行,任务是有序执行的;
串行队列,异步执行,开一个新的线程,任务是有序执行的;
并行队列,同步执行,不开线程,在当前线程下执行,任务顺序执行,等于串行队列,同步执行;
并行队列,异步执行,开多个线程,任务无序执行,效率最大;
主队列,异步执行,在主线线程执行,任务顺序执行;
主队列,同步执行,死锁(程序处于互相等待的状态);
GCD中有一个系统定义的全局并发队列;dispatch_get_global_queue(0,0);第一个参数时优先级;是为了兼容性考虑赋值0,因为IOS8之前和之后的定义有不用;第二个参数,没有意义,方便给未来使用;
GCD中有一个队列组:
//创建组
dispatch_group_tgroup =dispatch_group_create();
//开启异步任务,将任务添加至组任务。参数1:组,参数2:队列,参数3:任务
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"LOL1.zip %@",[NSThreadcurrentThread]);
});
//当组任务执行完成后,便可以执行其他的任务;
dispatch_group_notify(group,dispatch_get_main_queue(), ^{
NSLog(@"下载完成%@",[NSThreadcurrentThread]);
});
6、执行唯一一次:dispatch_once,block里面的代码无论调用多少次,都只会执行一次;有线程安全;
static dispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
<#code#>
});
可以使用这种方法实现单例模式;