一、线程的创建
#pragma mark - 线程的三种创建方法
// 创建线程方式1
- (void)createThread1{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(task) object:nil];
[thread start];
}
// 创建线程方式2
- (void)createThread2{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(task) object:nil];
[thread start];
}
// 创建线程方式3 隐式创建线程
- (void)createThread3{
[self performSelectorInBackground:@selector(task) withObject:nil];
}
二、线程的状态
线程状态有就绪(runnable)、运行(running)、阻塞(blocked)、死亡(Dead)四种状态

三、控制线程状态
1. 启动线程
-(void)start; 进入就绪状态 ->运行状态。当线程任务执行完毕,自动进入死亡状态
2. 阻塞(暂停)线程
-(void)sleepUntilDate:(NSDate *)date;
-(void)sleepForTimeInterval:(NSTimeInterval)ti;
进入阻塞状态
3. 强制线程停止
- (void)exit;
进入死亡状态
值得注意的是,一旦线程死亡,就不能再次开启任务
// 创建线程方式1
- (void)createThread1{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(task) object:nil];
[thread start];
}
/**
线程上运行都的方法
*/
-(void)task{
NSLog(@"task is running %@",[NSThread currentThread]);
// 阻塞
NSLog(@"线程睡了");
[NSThread sleepForTimeInterval:1];
NSLog(@"线程睡醒了");
//死亡
// 手动杀死线程
[NSThread exit];
NSLog(@"执行试一下");
}
四、线程的优先级
0.0 - 1.0 1.0是最高的默认是0.5。优先级高的线程不一定比优先级低的线程先执行,优先级高只是说明该线程有更多的可能被CPU执行。
/**
线程的优先级
*/
-(void)priorityTest{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(task) object:nil];
// name属性调试方便
thread.name = @"thread————————>1";
// 0.0 - 1.0 1.0是最高的默认是0.5
// 优先级高的线程不一定比优先级低的线程先执行,优先级高只是说明该线程有更多的可能被CPU执行。
thread.threadPriority = 0;
[thread start];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(task) object:nil];
thread2.name = @"thread2";
[thread2 start];
}
五、安全隐患示例
- 银行存款1000元,同事异地取款各1000元这时候,现实的余额可能就会有偏差
- 火车票余票100张,两个终端各购买一张车票,余票数字显示可能会有问题
六、共享变量的问题
// nonatomic非原子属性:多线程环境下,多个线程可以同事读取和赋值
// atomic原子属性:多线程环境下,只有一个线程能对变量进行赋值,多个线程可以同事读取
// 相当于在set方法里面加了一个同步锁
// atomic我们称之为自旋锁
// 问题:自旋锁可不可以代替同步锁?
@property (atomic, strong) NSObject *obj1;
@property (nonatomic, strong) NSObject *obj2;
/**
线程的优先级
*/
-(void)fakeSellTicketTest{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
// name属性调试方便
thread.name = @"thread————————>1";
// 0.0 - 1.0 1.0是最高的默认是0.5
// 优先级高的线程不一定比优先级低的线程先执行,优先级高只是说明该线程有更多的可能被CPU执行。
thread.threadPriority = 0;
[thread start];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
thread2.name = @"thread2";
[thread2 start];
}
- (void)sellTicket{
while (YES) {
[NSThread sleepForTimeInterval:1];
// 锁是一个任意对象,默认锁是开着的
// 同步锁(互斥锁)
@synchronized (self) {
if (self.tickets > 0 ) {
self.tickets = self.tickets - 1;
NSLog(@"%@ 余票%d", [NSThread currentThread], self.tickets);
continue;
}
}
NSLog(@"票卖完了");
break;
}
}
七、安全隐患的解决 - 互斥锁
- 互斥锁的使用
@synchronized(锁对象){ //需要锁定的代码 }
需要注意的是,锁定一份代码只用一把锁,用多把锁是无效的 - 互斥锁的优缺点
优点:能有效的防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源 - 互斥锁的使用前提:锁条线程抢夺同一块资源
- 专业术语
线程同步的意思是:多条线程按顺序地执行任务
互斥锁:就是使用了线程的同步技术
八、原子和非原子属性
- OC在定义属性时有nonatomic和atomic两种选择
- atomic:原子属性,为setter方法加锁(默认就是atomic)
- nonatomic:非原子属性,不会为setter方法加锁
- iOS开发建议
- 所有属性都声明为nonatomic
- 尽量避免多线程抢夺同一块资源
- 尽量将加锁,资源抢夺的业务逻辑交给服务器去处理,减小移动客户端的压力
九、互斥锁和自旋锁
- 互斥锁:如果返现起亚线程正在执行锁定代码,线程会进入休眠(就绪状态),等其它线程时间片到打开锁后,线程会被唤醒(执行)
- 自旋锁:如果返现有其他线程正在锁定发吗,线程会用死循环的方式,一直等待锁定的代码执行完成,自旋锁更适合执行不耗时的代码
十、线程安全
- 线程同时操作是不安全的,多个线程同一个全局变量
- 线程安全:在多个线程进行读写操作时,仍然能够保证数据的正确
十一、UI线程
- 几乎所有的UIkit的类都不是线程安全的
- 在主线程上执行所有的更新UI的操作
- mutable线程不安全的
十二、线程间通讯
- 什么叫做线程间通讯
- 在一个进程中,线程往往不是孤立存在的,多个线程之间往往需要通信
- 线程间通讯的具体体现
- 一个线程传递数据给另一个线程
- 在一个线程中执行完特定的任务后,转到另一个线程继续执行任务
- 线程间常用的通信方法
-(void)performSelectorOnMainThread:(SEL)aSelector
withObject:(id)arg waitUntilDone: (BOOL)wait;
-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait; - 具体示例 - 利用NSThread异步下载图片
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, weak) UIImageView *imgV;
@property (nonatomic, weak) UIScrollView *scrlView;
@end
@implementation ViewController
// 重写次方法,默认不会加载XIB和SB
- (void)loadView{
[self createUI];
}
- (void)viewDidLoad {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downLoadImg) object:nil];
[thread start];
}
- (void)createUI{
UIScrollView *scrlView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.scrlView = scrlView;
self.view = scrlView;
UIImageView *imgV = [[UIImageView alloc] init];
self.imgV = imgV;
[self.view addSubview:imgV];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
}
-(void)downLoadImg{
[NSThread sleepForTimeInterval:1];
NSString *str = @"https://desk-fd.zol-img.com.cn/t_s1280x800c5/g5/M00/02/04/ChMkJ1bKx4qIGTZLAAmjEID8lj0AALH1ADgWvsACaMo983.jpg";
NSURL *url = [NSURL URLWithString:str];
// 获取图片资源
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
// 线程间通信 子线程 -> 主线程
//参数1: 方法
//参数2: 方法参数
//参数3: 等待执行完成
[self performSelectorOnMainThread:@selector(updateUI:) withObject:img waitUntilDone: NO];
}
- (void)updateUI:(UIImage *)img{
self.imgV.image = img;
// 根据图片的大小显示图片
[self.imgV sizeToFit];
[self.scrlView setContentSize:img.size];
}
@end
网友评论