• NSThread(II)


    非线程安全

     1 //初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
     2 - (void)initTicketStatusNotSave {
     3     // 1. 设置剩余火车票为 50
     4     self.ticketSurplusCount = 10;
     5     
     6     // 2. 设置北京火车票售卖窗口的线程
     7     self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketNotSafe) object:nil];
     8     self.ticketSaleWindow1.name = @"北京火车票售票窗口";
     9     
    10     // 3. 设置上海火车票售卖窗口的线程
    11     self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketNotSafe) object:nil];
    12     self.ticketSaleWindow2.name = @"上海火车票售票窗口";
    13     
    14     // 4. 开始售卖火车票
    15     [self.ticketSaleWindow1 start];
    16     [self.ticketSaleWindow2 start];
    17     
    18 }
    19 
    20 /**
    21  * 售卖火车票(非线程安全)
    22  */
    23 - (void)saleTicketNotSafe {
    24     while (1) {
    25         //如果还有票,继续售卖
    26         if (self.ticketSurplusCount > 0) {
    27             self.ticketSurplusCount --;
    28             NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
    29             [NSThread sleepForTimeInterval:0.2];
    30         }
    31         //如果已卖完,关闭售票窗口
    32         else {
    33             NSLog(@"所有火车票均已售完");
    34             break;
    35         }
    36     }
    37 }
    View Code

    打印结果:

     1 <-------------------------NSThread线程结束------------------------2018-04-04 13:44:34.640134+0800 StruggleSwift[3660:189882] 剩余票数:9 窗口:北京火车票售票窗口
     2 -->
     3 2018-04-04 13:44:37.602762+0800 StruggleSwift[3660:189887] 剩余票数:8 窗口:上海火车票售票窗口
     4 2018-04-04 13:44:39.144970+0800 StruggleSwift[3660:189882] 剩余票数:6 窗口:北京火车票售票窗口
     5 2018-04-04 13:44:39.144970+0800 StruggleSwift[3660:189887] 剩余票数:6 窗口:上海火车票售票窗口
     6 2018-04-04 13:44:40.939316+0800 StruggleSwift[3660:189882] 剩余票数:5 窗口:北京火车票售票窗口
     7 2018-04-04 13:44:40.939316+0800 StruggleSwift[3660:189887] 剩余票数:5 窗口:上海火车票售票窗口
     8 2018-04-04 13:44:42.343211+0800 StruggleSwift[3660:189887] 剩余票数:4 窗口:上海火车票售票窗口
     9 2018-04-04 13:44:42.343219+0800 StruggleSwift[3660:189882] 剩余票数:4 窗口:北京火车票售票窗口
    10 2018-04-04 13:44:43.646726+0800 StruggleSwift[3660:189887] 剩余票数:3 窗口:上海火车票售票窗口
    11 2018-04-04 13:44:43.646728+0800 StruggleSwift[3660:189882] 剩余票数:3 窗口:北京火车票售票窗口
    12 2018-04-04 13:44:45.168354+0800 StruggleSwift[3660:189882] 剩余票数:2 窗口:北京火车票售票窗口
    13 2018-04-04 13:44:45.168361+0800 StruggleSwift[3660:189887] 剩余票数:2 窗口:上海火车票售票窗口
    14 2018-04-04 13:44:47.232723+0800 StruggleSwift[3660:189882] 剩余票数:0 窗口:北京火车票售票窗口
    15 2018-04-04 13:44:47.232735+0800 StruggleSwift[3660:189887] 剩余票数:0 窗口:上海火车票售票窗口
    16 2018-04-04 13:44:49.086100+0800 StruggleSwift[3660:189882] 所有火车票均已售完
    17 2018-04-04 13:44:49.086100+0800 StruggleSwift[3660:189887] 所有火车票均已售完

    总结:可以看到在线程不安全的情况下,得到票数是错乱的,这样显然不符合我们的需求,所以我们需要考虑线程安全问题。 

    NSThread 线程安全

    线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。为了简单起见,这里不对各种锁的解决方案和性能做分析,只用最简单的@synchronized来保证线程安全,从而解决线程同步问题。

     1 //初始化火车票数量、卖票窗口(线程安全)、并开始卖票
     2 - (void)initTicketStatusSave {
     3     // 1. 设置剩余火车票为 50
     4     self.ticketSurplusCount = 10;
     5     
     6     // 2. 设置北京火车票售卖窗口的线程
     7     self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
     8     self.ticketSaleWindow1.name = @"北京火车票售票窗口";
     9     
    10     // 3. 设置上海火车票售卖窗口的线程
    11     self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
    12     self.ticketSaleWindow2.name = @"上海火车票售票窗口";
    13     
    14     // 4. 开始售卖火车票
    15     [self.ticketSaleWindow1 start];
    16     [self.ticketSaleWindow2 start];
    17     
    18 }
    19 
    20 /**
    21  * 售卖火车票(线程安全)
    22  */
    23 - (void)saleTicketSafe {
    24     while (1) {
    25         // 互斥锁
    26         @synchronized (self) {
    27             //如果还有票,继续售卖
    28             if (self.ticketSurplusCount > 0) {
    29                 self.ticketSurplusCount --;
    30                 NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
    31                 [NSThread sleepForTimeInterval:0.2];
    32             }
    33             //如果已卖完,关闭售票窗口
    34             else {
    35                 NSLog(@"所有火车票均已售完");
    36                 break;
    37             }
    38         }
    39     }
    40 }
    View Code

    打印结果:

     1 2018-04-09 10:38:20.960196+0800 StruggleSwift[1190:23146] 剩余票数:9 窗口:北京火车票售票窗口
     2 2018-04-09 10:38:22.523697+0800 StruggleSwift[1190:23147] 剩余票数:8 窗口:上海火车票售票窗口
     3 <-------------------------NSThread线程结束-------------------------->
     4 2018-04-09 10:38:27.273431+0800 StruggleSwift[1190:23146] 剩余票数:7 窗口:北京火车票售票窗口
     5 2018-04-09 10:38:27.273459+0800 StruggleSwift[1190:23147] 剩余票数:6 窗口:上海火车票售票窗口
     6 2018-04-09 10:38:34.442708+0800 StruggleSwift[1190:23683] 剩余票数:5 窗口:北京火车票售票窗口
     7 2018-04-09 10:38:34.442731+0800 StruggleSwift[1190:23146] 剩余票数:4 窗口:北京火车票售票窗口
     8 2018-04-09 10:38:34.442731+0800 StruggleSwift[1190:23147] 剩余票数:3 窗口:上海火车票售票窗口
     9 2018-04-09 10:38:34.647692+0800 StruggleSwift[1190:23147] 剩余票数:2 窗口:上海火车票售票窗口
    10 2018-04-09 10:38:34.647657+0800 StruggleSwift[1190:23146] 剩余票数:1 窗口:北京火车票售票窗口
    11 2018-04-09 10:38:36.308703+0800 StruggleSwift[1190:23694] 剩余票数:0 窗口:上海火车票售票窗口
    12 2018-04-09 10:38:36.512623+0800 StruggleSwift[1190:23683] 所有火车票均已售完
    13 2018-04-09 10:38:36.512589+0800 StruggleSwift[1190:23146] 所有火车票均已售完
    14 2018-04-09 10:38:36.512572+0800 StruggleSwift[1190:23147] 所有火车票均已售完
    15 2018-04-09 10:38:37.557290+0800 StruggleSwift[1190:23694] 所有火车票均已售完

    结论:在考虑了线程安全的情况下,加锁之后,得到的票数是正确的,没有出现混乱的情况。

    线程的状态转换

    SThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];,在内存中的表现为: 

     

    当调用[thread start];后,系统把线程对象放入可调度线程池中,线程对象进入就绪状态,如下图所示。

    当然,可调度线程池中,会有其他的线程对象,如下图所示。在这里我们只关心左边的线程对象。

    当前线程的状态转换

      a. 如果CPU现在调度当前线程对象,则当前线程对象进入运行状态,如果CPU调度其他线程对象,则当前线程对象回到就绪状态。

      b. 如果CPU在运行当前线程对象的时候调用了sleep方法等待同步锁,则当前线程对象就进入了阻塞状态,等到sleep到时得到同步锁,则回到就绪状态。

      c. 如果CPU在运行当前线程对象的时候线程任务执行完毕异常强制退出,则当前线程对象进入死亡状态。

    只看文字可能不太好理解,具体当前线程对象的状态变化如下图所示。

  • 相关阅读:
    贪心算法过河问题 pojo1700
    大脑的合理使用
    给自己的忠言
    篮子水果模拟消费者生产者
    线程安全高效的单例模式
    Java提高篇——JVM加载class文件的原理机制
    递归的研究
    虚拟机分区方法
    使用spark dataSet 和rdd 解决 某个用户在某个地点待了多长时间
    获取数据集的好的方
  • 原文地址:https://www.cnblogs.com/EchoHG/p/8716852.html
Copyright © 2020-2023  润新知