多线程之线程安全
2016-07-28 本文已影响169人
kevinLY
前提
面试的时候,多线程的问题经常被问到,刚开始会问你iOS中实现多线程有几种方式;
答曰:NSthread、GCD、NSOperation+NSoperationQueue
进而会引出更深一个层次的问题?如何保证线程安全呢?
现象
相信做过服务器开发的同学经常会突然想到一个词:锁;
但是在iOS开发过程中,也许不怎么会关注这一块,但是相信大家都是用过nonatomic(非原子性)、atomic(原子性),并且更多的情况下用的都是nonatomic。
首先理解下为什么在移动开发的过程中一般都是使用nonatomic?这里就不多解释了
为什么要考虑多线程安全呢?通过下面常见的两个现象来看下线程安全可能带来的问题
现象一、取钱和存钱
老妈:小明,还有钱吗?
小明:我的卡里还有10000呢
老妈:那么少,再给你转点吧
小明:老妈爱你,再给转20000吧
老妈:现在就给你转,你待会去查查收到了吗
小明正打算取8000和朋友一起出去嗨,于是去附近的取款机取钱
想想老妈给小明转钱的同时,小明从取款机里取了8000,会出现什么问题?
1.pic.jpg
会出现两个结果:
1、老妈转账成功后,我却没有收到,余额:2000;
2、小明取了8000后,余额:30000;
先思考下什么情况下会出现这种现象;
现象二、卖火车票
多个售票员卖同一批火车票,简单的看下图吧(画的比较丑,凑合看吧)
2.pic.jpg
代码模拟
一、
@interface YTViewController ()
@property (nonatomic, assign) int leftMoney;
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSThread *thread2;
@end
@implementation YTViewController
- (instancetype)init
{
self = [super init];
if (self) {
self.leftMoney = 10000;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *but = [UIButton buttonWithType:UIButtonTypeCustom];
[self.view addSubview:but];
[but setFrame:self.view.bounds];
[but setBackgroundColor:[UIColor blueColor]];
[but setTitle:@"开始存取钱" forState:UIControlStateNormal];
[but addTarget:self action:@selector(btnStart:) forControlEvents:UIControlEventTouchUpInside];
//开启多个线程 模拟存取钱
self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(sellthread) object:nil];
self.thread1.name = @"老妈";
self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sellthread) object:nil];
self.thread2.name = @"小明";
}
- (void)btnStart:(UIButton *)btn
{
[self.thread1 start];
[self.thread2 start];
}
- (void)sellthread
{
//1、先检查余额
int money = self.leftMoney;
if (money > 0) {
//暂停一段时间 模拟业务处理耗时
[NSThread sleepForTimeInterval:0.02];
//获取线程信息
NSThread *thread = [NSThread currentThread];
if ([thread.name isEqualToString:@"小明"]) {
self.leftMoney = money - 8000;
NSLog(@"%@--取了8000,还剩余额%d", thread.name, self.leftMoney);
} else {
self.leftMoney = money + 20000;
NSLog(@"%@--存了20000,还剩余额%d", thread.name, self.leftMoney);
}
} else {
//退出线程
[NSThread exit];
}
}
打印结果:
3.pic.jpg二、
/*!
* @author yangL, 16-07-28 11:07:21
*
* @brief 线程安全
*/
#import "YTViewController.h"
@interface YTViewController ()
@property (nonatomic, assign) int leftTickets;
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSThread *thread2;
@property (nonatomic, strong) NSThread *thread3;
@end
@implementation YTViewController
- (instancetype)init
{
self = [super init];
if (self) {
self.leftTickets = 10;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *but = [UIButton buttonWithType:UIButtonTypeCustom];
[self.view addSubview:but];
[but setFrame:self.view.bounds];
[but setBackgroundColor:[UIColor blueColor]];
[but setTitle:@"开始售票" forState:UIControlStateNormal];
[but addTarget:self action:@selector(btnStart:) forControlEvents:UIControlEventTouchUpInside];
//开启多个线程 模拟售票员售票
self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(sellthread) object:nil];
self.thread1.name = @"售票员A";
self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sellthread) object:nil];
self.thread2.name = @"售票员B";
self.thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(sellthread) object:nil];
self.thread3.name = @"售票员C";
}
- (void)btnStart:(UIButton *)btn
{
[self.thread1 start];
[self.thread2 start];
[self.thread3 start];
}
- (void)sellthread
{
while (1) {
//1、先检查票数
int count = self.leftTickets;
if (count > 0) {
//暂停一段时间 模拟业务处理耗时
[NSThread sleepForTimeInterval:0.02];
//票数减1
self.leftTickets = count - 1;
//获取线程信息
NSThread *thread = [NSThread currentThread];
NSLog(@"%@--卖出一张票,还剩余%d张票", thread.name, self.leftTickets);
} else {
//退出线程
[NSThread exit];
}
}
}
打印结果:
4.pic_hd.jpg
如何解决
当然银行和铁道部不会让上面的情况发生;
我们法相上述两种情况都有一个共同的特点:多个对象共享一块公共的资源(现象一:余额 ;现象二:车票)
存钱和取钱相当于两个线程,如果存钱的时候把余额锁定,存钱的线程完成后才允许取钱的线程操作,就不会出现上述问题了;
可以使用互斥锁实现:@synchronized(锁对象) { // 需要锁定的代码}
关键代码和运行结果截图:
6.pic.jpg 5.pic_hd.jpg
不懂就药问