1.NSLock 线程锁,
任何两个线程访问同一共享资源(变量,数组)都需要加锁,保证同一时刻只能有一个线程访问共享资源
一个银行账户:有1000块钱,有两个线程同时做一次取钱操作,取钱的金额为800,这是就需要加锁,英文访问的是同一个银行账号
如果不加锁
@implementation FKAccount
- (id)initWithAccountNo:(NSString*)aAccount
balance:(CGFloat)aBalance
{
self = [super init];
if (self) {
_accountNo = aAccount;
_balance = aBalance;
}
return self;
}
// 提供一个draw方法来完成取钱操作
- (void) draw:(CGFloat)drawAmount
{
// 账户余额大于取钱数目
if (self.balance >= drawAmount)
{
// 吐出钞票
NSLog(@"%@取钱成功!吐出钞票:%g", [NSThread currentThread].name
, drawAmount);
// [NSThread sleepForTimeInterval:0.001];
// 修改余额
_balance = _balance - drawAmount;
NSLog(@"\t余额为: %g" , self.balance);
}
else
{
NSLog(@"%@取钱失败!余额不足!", [NSThread currentThread].name);
}
}
- (NSUInteger) hash
{
return [self.accountNo hash];
}
- (BOOL)isEqual:(id)anObject
{
if(self == anObject)
return YES;
if (anObject != nil
&& [anObject class] == [FKAccount class])
{
FKAccount* target = (FKAccount*)anObject;
return [target.accountNo isEqualToString:self.accountNo];
}
return NO;
}
所以银行账户需要加锁
// 封装账户编号、账户余额两个属性
@implementation FKAccount
NSLock* lock;
- (id)init
{
self = [super init];
if (self) {
lock = [[NSLock alloc] init];
}
return self;
}
- (id)initWithAccountNo:(NSString*)aAccount
balance:(CGFloat)aBalance
{
self = [super init];
if (self) {
lock = [[NSLock alloc] init];
_accountNo = aAccount;
_balance = aBalance;
}
return self;
}
// 提供一个线程安全的draw方法来完成取钱操作
- (void) draw:(CGFloat)drawAmount
{
// 显式锁定lock对象
[lock lock];
// 账户余额大于取钱数目
if (self.balance >= drawAmount)
{
// 吐出钞票
NSLog(@"%@取钱成功!吐出钞票:%g", [NSThread currentThread].name
, drawAmount);
[NSThread sleepForTimeInterval:0.001];
// 修改余额
_balance = _balance - drawAmount;
NSLog(@"\t余额为: %g" , self.balance);
}
else
{
NSLog(@"%@取钱失败!余额不足!", [NSThread currentThread].name);
}
// 释放lock的锁定
[lock unlock];
}
- (NSUInteger) hash
{
return [self.accountNo hash];
}
- (BOOL)isEqual:(id)anObject
{
if(self == anObject)
return YES;
if (anObject != nil
&& [anObject class] == [FKAccount class])
{
FKAccount* target = (FKAccount*)anObject;
return [target.accountNo isEqualToString:self.accountNo];
}
return NO;
}
@end
调用
FKAccount* account;
- (void)viewDidLoad
{
[super viewDidLoad];
// 创建一个账号
account = [[FKAccount alloc] initWithAccountNo:@"321231" balance: 1000.0];
}
- (IBAction)draw:(id)sender
{
// 创建第1个线程对象
NSThread* thread1 = [[NSThread alloc] initWithTarget:self
selector:@selector(drawMethod:)
object:[NSNumber numberWithInt:800]];
// 创建第2个线程对象
NSThread* thread2 = [[NSThread alloc] initWithTarget:self
selector:@selector(drawMethod:)
object:[NSNumber numberWithInt:800]];
// 启动2条线程
[thread1 start];
[thread2 start];
}
- (void) drawMethod:(NSNumber*) drawAmount
{
// 直接调用account对象的draw方法来执行取钱
[account draw:drawAmount.doubleValue];
}
2.同步锁synchronized
@implementation FKAccount
- (id)initWithAccountNo:(NSString*)aAccount
balance:(CGFloat)aBalance
{
self = [super init];
if (self) {
_accountNo = aAccount;
_balance = aBalance;
}
return self;
}
// 提供一个线程安全的draw方法来完成取钱操作
- (void) draw:(CGFloat)drawAmount
{
// 使用self作为同步监视器,任何线程进入下面同步代码块之前,
// 必须先获得对self账户的锁定——其他线程无法获得锁,也就无法修改它
// 这种做法符合:“加锁 → 修改 → 释放锁”的逻辑
@synchronized(self)
{
// 账户余额大于取钱数目
if (self.balance >= drawAmount)
{
// 吐出钞票
NSLog(@"%@取钱成功!吐出钞票:%g", [NSThread currentThread].name
, drawAmount);
[NSThread sleepForTimeInterval:0.001];
// 修改余额
_balance = _balance - drawAmount;
NSLog(@"\t余额为: %g" , self.balance);
}
else
{
NSLog(@"%@取钱失败!余额不足!", [NSThread currentThread].name);
}
}//同步代码块结束,该线程释放同步锁
}
- (NSUInteger) hash
{
return [self.accountNo hash];
}
- (BOOL)isEqual:(id)anObject
{
if(self == anObject)
return YES;
if (anObject != nil
&& [anObject class] == [FKAccount class])
{
FKAccount* target = (FKAccount*)anObject;
return [target.accountNo isEqualToString:self.accountNo];
}
return NO;
}
以上使用的主函数调用都一样的,只是FKAccount的实现方式不一样
3.NSCondition
使用NSCondition,实现多线程的同步,即,可实现生产者消费者问题。
基本思路是,首先要创建公用的NSCondition实例。然后:
以银行账号为例子
有存钱,有取钱操作,这个时候需要NSCondition
@implementation FKAccount
NSCondition* cond;
BOOL flag;
- (id)init
{
self = [super init];
if (self) {
cond = [[NSCondition alloc] init];
}
return self;
}
- (id)initWithAccountNo:(NSString*)aAccount
balance:(CGFloat)aBalance
{
self = [super init];
if (self) {
cond = [[NSCondition alloc] init];
_accountNo = aAccount;
_balance = aBalance;
}
return self;
}
// 提供一个线程安全的draw方法来完成取钱操作
- (void) draw:(CGFloat)drawAmount
{
// 加锁
[cond lock];
// 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
if (!flag)
{
[cond wait];
}
else
{
// 执行取钱
NSLog(@"%@ 取钱:%g" , [NSThread currentThread].name
,drawAmount);
_balance -= drawAmount;
NSLog(@"账户余额为:%g" , self.balance);
// 将标识账户是否已有存款的旗标设为NO。
flag = NO;
// 唤醒其他线程
[cond broadcast];
}
[cond unlock];
}
- (void) deposit:(CGFloat) depositAmount
{
[cond lock];
// 如果flag为YES,表明账户中已有人存钱进去,则存钱方法阻塞
if(flag) // ①
{
[cond wait];
}
else
{
// 执行存款
NSLog(@"%@ 存款:%g" , [NSThread currentThread].name
, depositAmount);
_balance += depositAmount;
NSLog(@"账户余额为:%g" , self.balance);
// 将表示账户是否已有存款的旗标设为YES
flag = YES;
// 唤醒其他线程
[cond broadcast];
}
[cond unlock];
}
- (NSUInteger) hash
{
return [self.accountNo hash];
}
- (BOOL)isEqual:(id)anObject
{
if(self == anObject)
return YES;
if (anObject != nil
&& [anObject class] == [FKAccount class])
{
FKAccount* target = (FKAccount*)anObject;
return [target.accountNo isEqualToString:self.accountNo];
}
return NO;
}
@end
存取钱操作
@implementation FKViewController
FKAccount* account;
- (void)viewDidLoad
{
[super viewDidLoad];
// 创建一个账号
account = [[FKAccount alloc] initWithAccountNo:@"321231" balance: 1000.0];
}
- (IBAction)depositDraw:(id)sender
{
// 创建、启动3个存钱的线程
[NSThread detachNewThreadSelector:@selector(drawMethod:) toTarget:self
withObject:[NSNumber numberWithDouble:800.0]];
[NSThread detachNewThreadSelector:@selector(drawMethod:) toTarget:self
withObject:[NSNumber numberWithDouble:800.0]];
// 创建、启动存钱的线程
[NSThread detachNewThreadSelector:@selector(drawMethod:) toTarget:self
withObject:[NSNumber numberWithDouble:800.0]];
// 创建、启动取钱的线程
[NSThread detachNewThreadSelector:@selector(depositMethod:) toTarget:self
withObject:[NSNumber numberWithDouble:800.0]];
}
- (void) drawMethod:(NSNumber*) drawAmount
{
[NSThread currentThread].name = @"甲";
// 重复100次执行取钱操作
for (int i = 0 ; i < 100 ; i++ )
{
[account draw:drawAmount.doubleValue];
}
}
- (void) depositMethod:(NSNumber*) depositAmount
{
[NSThread currentThread].name = @"乙";
// 重复100次执行存款操作
for (int i = 0 ; i < 100 ; i++ )
{
[account deposit:depositAmount.doubleValue];
}
}
@end