非线程安全

 //初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
- (void)initTicketStatusNotSave {
// 1. 设置剩余火车票为 50
self.ticketSurplusCount = ; // 2. 设置北京火车票售卖窗口的线程
self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketNotSafe) object:nil];
self.ticketSaleWindow1.name = @"北京火车票售票窗口"; // 3. 设置上海火车票售卖窗口的线程
self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketNotSafe) object:nil];
self.ticketSaleWindow2.name = @"上海火车票售票窗口"; // 4. 开始售卖火车票
[self.ticketSaleWindow1 start];
[self.ticketSaleWindow2 start]; } /**
* 售卖火车票(非线程安全)
*/
- (void)saleTicketNotSafe {
while () {
//如果还有票,继续售卖
if (self.ticketSurplusCount > ) {
self.ticketSurplusCount --;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
[NSThread sleepForTimeInterval:0.2];
}
//如果已卖完,关闭售票窗口
else {
NSLog(@"所有火车票均已售完");
break;
}
}
}

打印结果:

 <-------------------------NSThread线程结束-------------------------- ::34.640134+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-->
-- ::37.602762+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::39.144970+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::39.144970+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::40.939316+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::40.939316+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::42.343211+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::42.343219+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::43.646726+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::43.646728+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::45.168354+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::45.168361+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::47.232723+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::47.232735+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::49.086100+ StruggleSwift[:] 所有火车票均已售完
-- ::49.086100+ StruggleSwift[:] 所有火车票均已售完

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

NSThread 线程安全

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

 //初始化火车票数量、卖票窗口(线程安全)、并开始卖票
- (void)initTicketStatusSave {
// 1. 设置剩余火车票为 50
self.ticketSurplusCount = ; // 2. 设置北京火车票售卖窗口的线程
self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
self.ticketSaleWindow1.name = @"北京火车票售票窗口"; // 3. 设置上海火车票售卖窗口的线程
self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
self.ticketSaleWindow2.name = @"上海火车票售票窗口"; // 4. 开始售卖火车票
[self.ticketSaleWindow1 start];
[self.ticketSaleWindow2 start]; } /**
* 售卖火车票(线程安全)
*/
- (void)saleTicketSafe {
while () {
// 互斥锁
@synchronized (self) {
//如果还有票,继续售卖
if (self.ticketSurplusCount > ) {
self.ticketSurplusCount --;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
[NSThread sleepForTimeInterval:0.2];
}
//如果已卖完,关闭售票窗口
else {
NSLog(@"所有火车票均已售完");
break;
}
}
}
}

打印结果:

 -- ::20.960196+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::22.523697+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
<-------------------------NSThread线程结束-------------------------->
-- ::27.273431+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::27.273459+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::34.442708+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::34.442731+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::34.442731+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::34.647692+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::34.647657+ StruggleSwift[:] 剩余票数: 窗口:北京火车票售票窗口
-- ::36.308703+ StruggleSwift[:] 剩余票数: 窗口:上海火车票售票窗口
-- ::36.512623+ StruggleSwift[:] 所有火车票均已售完
-- ::36.512589+ StruggleSwift[:] 所有火车票均已售完
-- ::36.512572+ StruggleSwift[:] 所有火车票均已售完
-- ::37.557290+ StruggleSwift[:] 所有火车票均已售完

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

线程的状态转换

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

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

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

当前线程的状态转换

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

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

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

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

NSThread(II)的更多相关文章

  1. ios 多线程之NSThread篇举例详解

    这篇博客是接着总篇iOS GCD NSOperation NSThread等多线程各种举例详解写的一个支篇.总篇也包含了此文的链接.本文讲解的知识点有NSThread的开始.取消.在当前线程执行任务. ...

  2. Leetcode 笔记 113 - Path Sum II

    题目链接:Path Sum II | LeetCode OJ Given a binary tree and a sum, find all root-to-leaf paths where each ...

  3. Leetcode 笔记 117 - Populating Next Right Pointers in Each Node II

    题目链接:Populating Next Right Pointers in Each Node II | LeetCode OJ Follow up for problem "Popula ...

  4. 函数式Android编程(II):Kotlin语言的集合操作

    原文标题:Functional Android (II): Collection operations in Kotlin 原文链接:http://antonioleiva.com/collectio ...

  5. 4.1/4.2 多线程进阶篇<上>(Pthread & NSThread)

    本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人 “简书” 本文源码 Demo 详见 Githubhttps://github.com/shorfng ...

  6. 统计分析中Type I Error与Type II Error的区别

    统计分析中Type I Error与Type II Error的区别 在统计分析中,经常提到Type I Error和Type II Error.他们的基本概念是什么?有什么区别? 下面的表格显示 b ...

  7. hdu1032 Train Problem II (卡特兰数)

    题意: 给你一个数n,表示有n辆火车,编号从1到n,入站,问你有多少种出站的可能.    (题于文末) 知识点: ps:百度百科的卡特兰数讲的不错,注意看其参考的博客. 卡特兰数(Catalan):前 ...

  8. [LeetCode] Guess Number Higher or Lower II 猜数字大小之二

    We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...

  9. [LeetCode] Number of Islands II 岛屿的数量之二

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

随机推荐

  1. Java 第二次测试总结

    Java 第二次测试总结 1. 相关知识点总结 Java测试题循环与递归知识点 补充知识点: for循环语句:for(表达式1:表达式2:表达式3)表达式一负责完成变量的初始化!表达式2是值为bool ...

  2. 如何实现一个字符的反转 (Java)

    一..通过jdk自带reverse的方法.

  3. js中属性类型:数据属性与访问器属性

    js中属性类型分为两种:数据属性和访问器属性 在js中,对象都是由名值对构成的,名:就是我们所说的属性名,值就是属性对应的值(基本值.对象.方法). ECMA-262第5版定义了只有内部才用的特性,描 ...

  4. h5 中的 section 标签

    转自 http://www.studyofnet.com/news/331.html 本文导读:<section> 标签定义文档中的节(section.区段).比如章节.页眉.页脚或文档中 ...

  5. Java基础知识(JAVA基本数据类型包装类)

    基本数据类型的包装类 为什么需要包装类? Java并不是纯面向对象的语言.Java语言是一个面向对象的语言,但是Java的基本数据类型却不是面向对象的.但是我们在实际使用中经常需要将基本数据转化成对象 ...

  6. git bash字体设置

    Git工具安装完毕之后,快速启动栏目显示有Git Bash.Git CMD和Git GUI.其中,Git Bash是跟Linux命令一样的,输入命令形式的.但是,有时输入命令显示的字体太小,需要对其进 ...

  7. RN-系列

    8081端口被占用,McAfee Agent关不掉 sudo lsof -n -i4TCP:8081 sudo launchctl list | grep 5693 sudo launchctl re ...

  8. 多线程——newFixedThreadPool线程池

    newFixedThreadPool线程池: 理解: 1.固定线程数的线程池. 2.通过Executors中的静态方法创建:     public static ExecutorService new ...

  9. Linux ssh将命令放入后台

    如何在关闭ssh连接的情况下,让程序继续运行? 对Unix,Linux类服务器维护经常是通过ssh完成的,而有些操作比较费时,如更新程序等.此时如果断开ssh连接的话,更新程序就会随之被中断.如何保证 ...

  10. 2019.03.25 git

    Git简介: Git是一个分布式版本控制软件. 背景故事: Linus在1991年创建了开源的Linux. 在2002年以前:世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后Linu ...