1、线程的创建
NSThread有三种创建方式,第一种是对象方法,返回一个NSThread对象。可以对该对象进行详细的设置。但是必须通过start方法启动线程。第二种是类方法,只是简化了第一中创建方式。第三种属于隐式创建线程。
1、创建新的线程
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
2、创建新的线程并自动启动线程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
3、隐式创建线程
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
代码示例如下:
- (void)thread1 {
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:@"线程1参数"];
thread1.name = @"线程1";
[thread1 start];
}
- (void)thread2 {
[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"线程2参数"];
}
- (void)thread3 {
[self performSelectorInBackground:@selector(test:) withObject:@"线程3参数"];
}
- (void)test:(NSString *)pra {
for (int i = 0; i < 5; i++) {
NSLog(@"%@, %@",pra,[NSThread currentThread]);
}
}
执行结果
2018-11-06 16:12:14.163745+0800 iOS NSThread[5928:201117] 线程1参数, 参数:<NSThread: 0x604000274e40>{number = 3, name = 线程1}
2018-11-06 16:12:14.163745+0800 iOS NSThread[5928:201118] 线程2参数, 参数:<NSThread: 0x604000274b80>{number = 4, name = (null)}
2018-11-06 16:12:14.164827+0800 iOS NSThread[5928:201117] 线程1参数, 参数:<NSThread: 0x604000274e40>{number = 3, name = 线程1}
2018-11-06 16:12:14.165539+0800 iOS NSThread[5928:201118] 线程2参数, 参数:<NSThread: 0x604000274b80>{number = 4, name = (null)}
2018-11-06 16:12:14.165576+0800 iOS NSThread[5928:201119] 线程3参数, 参数:<NSThread: 0x604000274480>{number = 5, name = (null)}
2018-11-06 16:12:14.165805+0800 iOS NSThread[5928:201117] 线程1参数, 参数:<NSThread: 0x604000274e40>{number = 3, name = 线程1}
2018-11-06 16:12:14.165887+0800 iOS NSThread[5928:201118] 线程2参数, 参数:<NSThread: 0x604000274b80>{number = 4, name = (null)}
2018-11-06 16:12:14.166871+0800 iOS NSThread[5928:201118] 线程2参数, 参数:<NSThread: 0x604000274b80>{number = 4, name = (null)}
2018-11-06 16:12:14.166950+0800 iOS NSThread[5928:201117] 线程1参数, 参数:<NSThread: 0x604000274e40>{number = 3, name = 线程1}
2018-11-06 16:12:14.167294+0800 iOS NSThread[5928:201117] 线程1参数, 参数:<NSThread: 0x604000274e40>{number = 3, name = 线程1}
2018-11-06 16:12:14.167384+0800 iOS NSThread[5928:201118] 线程2参数, 参数:<NSThread: 0x604000274b80>{number = 4, name = (null)}
2018-11-06 16:12:14.167009+0800 iOS NSThread[5928:201119] 线程3参数, 参数:<NSThread: 0x604000274480>{number = 5, name = (null)}
2018-11-06 16:12:14.169931+0800 iOS NSThread[5928:201119] 线程3参数, 参数:<NSThread: 0x604000274480>{number = 5, name = (null)}
2018-11-06 16:12:14.171300+0800 iOS NSThread[5928:201119] 线程3参数, 参数:<NSThread: 0x604000274480>{number = 5, name = (null)}
2018-11-06 16:12:14.171556+0800 iOS NSThread[5928:201119] 线程3参数, 参数:<NSThread: 0x604000274480>{number = 5, name = (null)}
2、线程的状态
线程被创建之后,并不是一开始就进入了执行状态,也不是一直处于执行状态。即便线程开始运行之后,也不会一直占用着CPU独立运行。由于CPU会在不用的线程之间来回切换,所以线程的状态也会进行切换。
线程大体有如下五种状态,分别如下:
- 新建(NEW)
当程序创建了一个新线程之后,该程序就会处于新建状态,这时它和其他对象一样,仅仅会系统分配了内存,并初始化了其内部成员变量的值,此时的线程没有任何动态特征。 - 就绪(Runable)
当线程调用了star方法之后,该线程就处于就绪状态系统会为其创建方法调用的栈和程序计数器,处于这种状态的线程并没有开始运行,只是代表可以运行了。但是具体什么时候开始运行,由系统进行控制。 - 运行(Running)
当CPU调度当前线程的时候,将其他线程挂起,当前线程变成运行状态。当CPU调度其他线程的时候,当前线程处于就绪状态,要测试某个线程是否正在运行,可以调用isExecuting方法,YES为该线程处于就绪状态。 - 终止(Exit)
当线程遇到以下三种情况时,线程就会由运行状态切换到终止状态。-
线程任务执行完成,线程正常结束
-
线程执行的过程中出现了异常,线程崩溃结束
-
直接调用NSThread的exit方法来终止当前正在执行的线程
要测试某个线程是否为结束状态,可以调用isFinished方法判断。返回YES表示该线程已经被终止
-
- 阻塞(Blocked)
如果当前执行的线程需要暂停一段时间,并进入阻塞状态,可以通过以下两个类方法来完成。需要注意的是,在线程进入阻塞状态之后,在其睡眠的时间之内,该线程不会获得执行的机会,即便系统中没有其他可执行的线程,处于阻塞状态的线程也不会执行。
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
3、线程的资源共享
进程中的一块资源有可能会被多个线程同时访问,这里的资源包括对象、变量、文件等,当多个线程访问同一块资源时,会造成资源抢夺,引起数据混乱。最经典的就是卖火车票的例子。
为了防止数据混轮,可以在共同访问资源的部分添加同步锁,用于保护数据。此处使用synchronized,格式如下:
@synchronized (obj) {
///共享资源
}
此处的obj就是锁对象,锁对象就实现了多线程的监控,保证同一时刻只有一个线程执行。考虑到同步锁的生命周期推荐使用当前线程所在的控制器作为锁对象。
代码示例如下:
- (void)thread1 {
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:@"线程1参数"];
thread1.name = @"线程1";
[thread1 start];
}
- (void)thread2 {
[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"线程2参数"];
}
- (void)thread3 {
[self performSelectorInBackground:@selector(test:) withObject:@"线程3参数"];
}
- (void)test:(NSString *)pra {
while (true) {
@synchronized (self) {
[NSThread sleepForTimeInterval:2];
if (self.count > 0) {
self.count --;
NSLog(@"%@买了一张票, 剩余%ld张票",[NSThread currentThread], self.count);
} else {
NSLog(@"票已经卖完了~~~~");
return;
}
}
}
}
执行结果:
2018-11-06 17:06:36.173152+0800 iOS NSThread[6053:221913] <NSThread: 0x604000274180>{number = 3, name = 线程1}买了一张票, 剩余49张票
2018-11-06 17:06:38.174253+0800 iOS NSThread[6053:221914] <NSThread: 0x604000274380>{number = 4, name = (null)}买了一张票, 剩余48张票
2018-11-06 17:06:40.178706+0800 iOS NSThread[6053:221915] <NSThread: 0x604000273f40>{number = 5, name = (null)}买了一张票, 剩余47张票
2018-11-06 17:06:42.184201+0800 iOS NSThread[6053:221913] <NSThread: 0x604000274180>{number = 3, name = 线程1}买了一张票, 剩余46张票
2018-11-06 17:06:44.184771+0800 iOS NSThread[6053:221914] <NSThread: 0x604000274380>{number = 4, name = (null)}买了一张票, 剩余45张票
、、、、、、
2018-11-06 17:08:04.350988+0800 iOS NSThread[6053:221915] <NSThread: 0x604000273f40>{number = 5, name = (null)}买了一张票, 剩余5张票
2018-11-06 17:08:06.356514+0800 iOS NSThread[6053:221913] <NSThread: 0x604000274180>{number = 3, name = 线程1}买了一张票, 剩余4张票
2018-11-06 17:08:08.362055+0800 iOS NSThread[6053:221914] <NSThread: 0x604000274380>{number = 4, name = (null)}买了一张票, 剩余3张票
2018-11-06 17:08:10.367592+0800 iOS NSThread[6053:221915] <NSThread: 0x604000273f40>{number = 5, name = (null)}买了一张票, 剩余2张票
2018-11-06 17:08:12.369528+0800 iOS NSThread[6053:221913] <NSThread: 0x604000274180>{number = 3, name = 线程1}买了一张票, 剩余1张票
2018-11-06 17:08:14.372783+0800 iOS NSThread[6053:221914] <NSThread: 0x604000274380>{number = 4, name = (null)}买了一张票, 剩余0张票
2018-11-06 17:08:16.375276+0800 iOS NSThread[6053:221915] 票已经卖完了~~~~
2018-11-06 17:08:18.378405+0800 iOS NSThread[6053:221913] 票已经卖完了~~~~
2018-11-06 17:08:20.383918+0800 iOS NSThread[6053:221914] 票已经卖完了~~~~
4、线程之间的通讯
线程之间的通讯,主要体现在一个线程完成之后,转到另一个线程去执行任务没在线程转换的过程中,也将数据传递给另一个线程。
NSThread主要提供了两个比较常用的方法用于实现线程之间的通讯。定义如下:
///回到主线程
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
///在子线程调用
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
参数arg是当前执行方法所在的线程传递出去的数据。wait是一个BOOL值,用来指定当前线程是否阻塞,当为YES时,阻塞当前线程,直到其他线程执行完毕才会继续执行当前线程。当为NO,表示不阻塞这个线程。
网友评论