美文网首页
iOS基础知识梳理 - 多线程NSThread

iOS基础知识梳理 - 多线程NSThread

作者: babyloveblues | 来源:发表于2019-08-04 13:36 被阅读0次

    一、线程的创建

    #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];
        
    }
    

    五、安全隐患示例

    1. 银行存款1000元,同事异地取款各1000元这时候,现实的余额可能就会有偏差
    2. 火车票余票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;
        }
    }
    

    七、安全隐患的解决 - 互斥锁

    1. 互斥锁的使用
      @synchronized(锁对象){ //需要锁定的代码 }
      需要注意的是,锁定一份代码只用一把锁,用多把锁是无效的
    2. 互斥锁的优缺点
      优点:能有效的防止因多线程抢夺资源造成的数据安全问题
      缺点:需要消耗大量的CPU资源
    3. 互斥锁的使用前提:锁条线程抢夺同一块资源
    4. 专业术语
      线程同步的意思是:多条线程按顺序地执行任务
      互斥锁:就是使用了线程的同步技术

    八、原子和非原子属性

    1. OC在定义属性时有nonatomic和atomic两种选择
    • atomic:原子属性,为setter方法加锁(默认就是atomic)
    • nonatomic:非原子属性,不会为setter方法加锁
    1. iOS开发建议
    • 所有属性都声明为nonatomic
    • 尽量避免多线程抢夺同一块资源
    • 尽量将加锁,资源抢夺的业务逻辑交给服务器去处理,减小移动客户端的压力

    九、互斥锁和自旋锁

    • 互斥锁:如果返现起亚线程正在执行锁定代码,线程会进入休眠(就绪状态),等其它线程时间片到打开锁后,线程会被唤醒(执行)
    • 自旋锁:如果返现有其他线程正在锁定发吗,线程会用死循环的方式,一直等待锁定的代码执行完成,自旋锁更适合执行不耗时的代码

    十、线程安全

    • 线程同时操作是不安全的,多个线程同一个全局变量
    • 线程安全:在多个线程进行读写操作时,仍然能够保证数据的正确

    十一、UI线程

    • 几乎所有的UIkit的类都不是线程安全的
    • 在主线程上执行所有的更新UI的操作
    • mutable线程不安全的

    十二、线程间通讯

    1. 什么叫做线程间通讯
    • 在一个进程中,线程往往不是孤立存在的,多个线程之间往往需要通信
    1. 线程间通讯的具体体现
    • 一个线程传递数据给另一个线程
    • 在一个线程中执行完特定的任务后,转到另一个线程继续执行任务
    1. 线程间常用的通信方法
      -(void)performSelectorOnMainThread:(SEL)aSelector
      withObject:(id)arg waitUntilDone: (BOOL)wait;
      -(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    2. 具体示例 - 利用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
    

    相关文章

      网友评论

          本文标题:iOS基础知识梳理 - 多线程NSThread

          本文链接:https://www.haomeiwen.com/subject/hmxydctx.html