非线程安全

 //初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
- (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 Mysql连接(1)

    工具:Eclipse .mysql5.7 MySQL连接驱动:mysql-connector-java-5.1.27.jar    获取地址:https://dev.mysql.com/downloa ...

  2. vue router拦截器的简单使用

    之前,为了实现router跳转的每个页面的url上都带上addressCode,然后用了一下router拦截器,很好用,当然也可以专门封装一个方法来实现(跳转的页面上带有addressCode),不过 ...

  3. 牛客网Wannafly挑战赛25A 因子 数论

    正解:小学数学数论 解题报告: 传送门 大概会连着写几道相对而言比较简单的数学题,,,之后就会比较难了QAQ 所以这题相对而言还是比较水的,,, 首先这种题目不难想到分解质因数趴,, 于是就先对p和n ...

  4. 学习UDP

    参考博客:https://www.cnblogs.com/skyfsm/p/6287787.html 简单编写了 udp的小程序,udp_client 端通过终端输入字符,然后发送给udp_serve ...

  5. JavaWeb学习总结——文件上传和下载

    在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用 ...

  6. 【托业】【新托业TOEIC新题型真题】学习笔记3-题库二->P5-6

    --------------------------------------单词-------------------------------------- oppose vt. 反对:对抗,抗争 v ...

  7. vue中根据当前时间进行排序

    computed: { newdataList: function() { return this.sortKey(this.dataList, "addtime"); } }, ...

  8. Windows 下MySql Replication(复制)配置

    环境准备 到官网下载mysql-installer-web-community-5.7.21.0.msi并安装,选择MySql Workbench,记录安装时root输入的密码. 需要安装在两台机器上 ...

  9. print()与println()区别

    print 不会换行,println会换行 例如:print(a):print(b):结果为: abprintln(a):println(b):结果为: a b

  10. SQL _ Create Procedure

    -- ================================================ -- Template generated from Template Explorer usi ...