多线程 (三)iOS中的锁
锁的类别:互斥锁,递归锁,条件锁,自旋锁等
锁的实现方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信号量等
下面说一下常用的几种锁:
1.@synchronized:对象级别所,互斥锁,性能较差不推荐使用
@synchronized(这里添加一个OC对象,一般使用self) {
这里写要加锁的代码
}
@synchronized使用注意点
1.加锁的代码尽量少
2.添加的OC对象必须在多个线程中都是同一对象,下面举一个反例
- (void)viewDidLoad {
[super viewDidLoad];
//设置票的数量为5
_tickets = 5;
//线程一
NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
threadOne.name = @"threadOne";
//线程二
NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
//开启线程
[threadOne start];
[threadTwo start];
}
- (void)saleTickets
{
NSObject *object = [[NSObject alloc] init];
while (1)
{
@synchronized(object) {
[NSThread sleepForTimeInterval:1];
if (_tickets > 0)
{
_tickets--;
NSLog(@"剩余票数= %ld",_tickets);
}
else
{
NSLog(@"票卖完了");
break;
}
}
}
}
结果卖票又出错了,出现这个原因的问题是每个线程都会创建一个object对象,锁后面加的object在不同线程中就不同了;

把@synchronized(object)改成 @synchronized(self)就能得到了正确结果

2.NSLock:互斥锁,
@interface ViewController ()
{
NSLock *mutexLock;
} @property (assign, nonatomic)NSInteger tickets;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
//创建锁
mutexLock = [[NSLock alloc] init]; //设置票的数量为5
_tickets = 5;
//线程一
NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
threadOne.name = @"threadOne";
//线程二
NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; //开启线程
[threadOne start];
[threadTwo start];
}
- (void)saleTickets
{
while (1)
{
[NSThread sleepForTimeInterval:1];
//加锁
[mutexLock lock];
if (_tickets > 0)
{
_tickets--;
NSLog(@"剩余票数= %ld",_tickets);
}
else
{
NSLog(@"票卖完了");
break;
}
//解锁
[mutexLock unlock];
}
}
NSLock: 使用注意,不能多次调用 lock方法,会造成死锁
3.NSRecursiveLock:递归锁
@interface ViewController ()
{
NSRecursiveLock *rsLock;
} @property (assign, nonatomic)NSInteger tickets; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
//创建锁递归锁
rsLock = [[NSRecursiveLock alloc] init];
//设置票的数量为5
_tickets = 5;
//线程一
NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
threadOne.name = @"threadOne";
//线程二
NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];
//开启线程
[threadOne start];
[threadTwo start];
}
- (void)saleTickets
{
while (1)
{
[NSThread sleepForTimeInterval:1];
//加锁,递归锁可以多次加锁
[rsLock lock];
[rsLock lock];
if (_tickets > 0)
{
_tickets--;
NSLog(@"剩余票数= %ld",_tickets);
}
else
{
NSLog(@"票卖完了");
break;
}
//解锁,只有对应次数解锁,其他线程才能访问。
[rsLock unlock];
[rsLock unlock];
}
}
4.NSConditionLock:条件锁
NSConditionLock:条件锁,一个线程获得了锁,其它线程等待。
[xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执行此行以下代码,如果已经有其他线程获得锁(可能是条件锁,或者无条件锁),则等待,直至其他线程解锁
[xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直至它解锁。
[xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件
@interface ViewController () {
NSConditionLock *_cdtLock; //条件锁
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建条件锁
_cdtLock = [[NSConditionLock alloc] init];
[NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];
}
- (void)conditionLockAction1 {
//阻塞线程2s
[NSThread sleepForTimeInterval:2];
for (NSInteger i = 0; i < 3; i++) {
//加锁
[_cdtLock lock];
NSLog(@"i = %li", i);
//释放锁,并设置condition属性的值为i
[_cdtLock unlockWithCondition:i];
}
}
- (void)conditionLockAction2 {
//当标识为2时同步代码段才能够执行,如果标识为其它数字则当前线程被阻塞。
[_cdtLock lockWhenCondition:2];
NSLog(@"thread2");
[_cdtLock unlock];
}
打印结果:
如果我们把代码中[_cdtLock lockWhenCondition:2]换成[_cdtLock lockWhenCondition:1]则会发现出现如下结果
和我们预想的在i = 1后面打印thread2不符合,这是因为conditionLockAction1中的代码段也需要获得锁,同时在循环执行过后把condition置成了2,那么conditionLockAction2就再也没机会加锁了,所以不打印thread2。
我们可以靠下面的代码验证
@interface ViewController () {
NSConditionLock *_cdtLock; //条件锁
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建条件锁
_cdtLock = [[NSConditionLock alloc] init];
[NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];
}
- (void)conditionLockAction1 {
//阻塞线程2s
[NSThread sleepForTimeInterval:2];
for (NSInteger i = 0; i < 3; i++) {
//加锁
[_cdtLock lock];
NSLog(@"i = %li", i);
//释放锁,并设置condition属性的值为i
[_cdtLock unlockWithCondition:i];
//在i 为 1的时候阻塞线程1s
if (i == 1)
{
[NSThread sleepForTimeInterval:1];
}
}
}
- (void)conditionLockAction2 {
//当标识为2时同步代码段才能够执行,如果标识为其它数字则当前线程被阻塞。
[_cdtLock lockWhenCondition:1];
NSLog(@"thread2");
[_cdtLock unlock];
}
现在的结果就和我们预期的一样了

5.NSCondition:可以理解为互斥锁和条件锁的结合
用生产者消费者中的例子可以很好的理解NSCondition
1.生产者要取得锁,然后去生产,生产后将生产的商品放入库房,如果库房满了,则wait,就释放锁,直到其它线程唤醒它去生产,如果没有满,则生产商品后调用signal,可以唤醒在此condition上等待的线程。
2.消费者要取得锁,然后去消费,如果当前没有商品,则wait,释放锁,直到有线程去唤醒它消费,如果有商品,则消费后会通知正在等待的生产者去生产商品。
生产者和消费者的关键是:当库房已满时,生产者等待,不再继续生产商品,当库房已空时,消费者等待,不再继续消费商品,走到库房有商品时,会由生产者通知消费来消费。
- (void)conditionTest
{
//创建数组存放商品
products = [[NSMutableArray alloc] init];
condition = [[NSCondition alloc] init]; [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];
} - (void)createConsumenr
{
while (1) {
//模拟消费商品时间,让它比生产慢一点
[NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 1.5];
[condition lock];
while (products.count == 0) {
NSLog(@"商品为0,等待生产");
[condition wait];
}
[products removeLastObject];
NSLog(@"消费了一个商品,商品数 = %ld",products.count);
[condition signal];
[condition unlock];
} } - (void)createProducter
{
while (1) {
//模拟生产商品时间
[NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 0.5];
[condition lock];
while (products.count == 5)
{
NSLog(@"商品满了,等待消费");
[condition wait];
}
[products addObject:[[NSObject alloc] init]];
NSLog(@"生产了一个商品,商品数%ld",products.count);
[condition signal];
[condition unlock];
} }
了解死锁
概念:死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
产生死锁的4个必要条件
多线程 (三)iOS中的锁的更多相关文章
- 多线程(三) iOS中的锁
锁的类别:互斥锁,递归锁,条件锁,自旋锁等 锁的实现方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信号量等 下面说一下常用的 ...
- 谈谈iOS中的锁
1 前言 近日工作不是太忙,刚好有时间了解一些其他东西,本来打算今天上午去体检,但是看看天气还是明天再去吧,也有很大一个原因:就是周六没有预约上!闲话少说,这里简单对锁来个简单介绍分享. 2 目录 第 ...
- 多线程(三) java中线程的简单使用
java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...
- JAVA多线程(三) 线程池和锁的深度化
github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-servic ...
- java多线程并发编程中的锁
synchronized: https://www.cnblogs.com/dolphin0520/p/3923737.html Lock:https://www.cnblogs.com/dolphi ...
- 深入理解 iOS 开发中的锁
来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大 ...
- Java并发编程(3) JUC中的锁
一 前言 前面已经说到JUC中的锁主要是基于AQS实现,而AQS(AQS的内部结构 .AQS的设计与实现)在前面已经简单介绍过了.今天记录下JUC包下的锁是怎么基于AQS上实现的 二 同步锁 同步锁不 ...
- iOS中的几种锁的总结,三种开启多线程的方式(GCD、NSOperation、NSThread)
学习内容 欢迎关注我的iOS学习总结--每天学一点iOS:https://github.com/practiceqian/one-day-one-iOS-summary OC中的几种锁 为什么要引入锁 ...
- 多线程在iOS开发中的应用
多线程基本概念 01 进程 进程是指在系统中正在运行的一个应用程序.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 02 线程 2-1 基本概念 1个进程要想执行任务,必须得有线程 ...
随机推荐
- web前端面试题收集(二)
简单介绍下你的前端代码开发与调试环境. Doctype声明的作用以及html4.01与html5中此声明的区别? 常用的块级元素与行内元素分别有哪些? 请画一下W3C盒模型 请写一个js函数,将url ...
- spring aop配置及用例说明(4)
欢迎交流转载:http://www.cnblogs.com/shizhongtao/p/3476161.html 这里简单对xml的配置方式做一下描述.代码还是上一篇(http://www.cnblo ...
- new失败判断
使用 malloc/calloc 等分配内存的函数时,一定要检查其返回值是否为空;但是C++ 里,如果 new 分配内存失败,默认是抛出bad_alloc异常,不会返回空:但是有些编译器对c++标准支 ...
- Contiki学习入门之概览
Contiki是专为物联网领域而设计的开源操作系统,适用于联网嵌入式系统和无线传感器网络.由瑞典计算机科学学院的Adam Dunkels团队开发.它有以下几个特点. 1. 网络标准 contiki提供 ...
- 《RHEL6.3 FTP服务器虚拟用户的配置(含图)》——如此简单
虚拟用户就是传说中的ftp服务vip用户,大致分为这么几步: 1.安装ftp软件包 yum install *ftp* 2.启动vsftpd服务 /etc/init.d/vsftpd restart ...
- OpenCV3添加滑动条和鼠标事件到图形界面
鼠标事件和滑动条控制在计算机视觉和OpenCV中非常有用,使用这些控件,用户可以直接与图形界面交互,改变输入图像或者变量的属性值. /* In this section, we are going t ...
- <script type="text/html"></script> js模版使用
<div></div> <script type="text/html" id="javascript_template"> ...
- amcharts 网页绘图插件
Amcharts是一组js图表,你可以免费使用在你的网站和基于网络的产品(非开源). Amcharts可以从简单的json提取数据,也可以从动态数据读取生成,比如PHP, .NET, Ruby on ...
- Servlet实现web站点文件下载功能示例
前段时间事情比较多,导致二月份没有记录自己的学习情况.最近接触了servlet,参考韩老师的教程自己写了一个web站点文件下载的小项目,该项目中还加入了简单的反盗链技术. 1.首先创建一个Shared ...
- .NET安装和配置Oracle数据访问组件(ODAC)
Many ASP.NET applications access Oracle database for the data source. Oracle supports the .NET with ...