标签:
本文要点
一、多线程安全隐患引出
假设火车站有3个卖票窗口,余票是1000,卖票窗口3个线程同一时刻读取剩余票数,都是读取的1000,卖票线程1卖了一张 ,余票变成999。卖票线程2反应慢点,在卖票线程1后面执行卖票,因为卖票线程2刚开始读取的余票也是1000,所以在卖掉一张后,余额也变成999。卖票线程3反应更慢,在卖票线程2后面执行卖票,因为卖票线程3刚开始读取的余票也是1000,所以在卖掉一张后,余额依旧也变成999。所以出现了错误,本来卖了3张,可是余票还有999张。
因此当多个线程访问同一块资源时(同一个对象、同一个变量、同一个文件),很容易引发数据错乱和数据安全问题。
二、多线程安全隐患代码示例
1>声明3个卖票线程,以及 leftTicketCount 来保存余票数
@property (nonatomic, strong) NSThread *thread1; //线程1 @property (nonatomic, strong) NSThread *thread2; //线程2 @property (nonatomic, strong) NSThread *thread3; //线程3 @property (nonatomic, assign) int leftTicketCount; //剩余票数
2>在viewDidLoad里面,设置余票为50张票,并创建了3个线程,并且分别起名“1号窗口”,“2号窗口” 及 “3号窗口”
self.leftTicketCount = 50; self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread1.name = @"1号窗?口";
self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread2.name = @"2号窗?口";
self.thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread3.name = @"3号窗?口";
3>实现售票方法
?- (void)saleTicket { while (1) { ?? int count = self.leftTicketCount;
if (count > 0) {
//线程睡?一下,其他线程才能有机可乘 [NSThread sleepForTimeInterval:0.05]; self.leftTicketCount = count - 1; NSLog(@"%@卖了?一张票, 剩余%d张票", [NSThread currentThread].name, self.leftTicketCount);
}else { return; // 退出循环
} ????????? }
}
输出结果为:
从结果可以轻易看出多个线程访问并操作剩余票数的时候,引发了数据错乱和数据安全问题
三、多线程安全隐患解决方案
在线程A读取数据后,加一把锁,别的线程就不能访问了,只允许加锁的线程A访问。当这个加锁线程操作完数据后,线程A解锁,此时别的线程就能访问了, 假设轮到线程B访问,线程B也先加把锁,保证只有自己能访问,执行完操作再解锁....就这样循环往复,只要谁访问就加把锁,直到操作结束后再解锁。这就是互斥锁。
加锁的目的就是为了保证同一时间,只能一个线程访问并执行代码。
代码实现互斥锁
- (void)saleTicket { while (1) { // ()?小括号?里?面放的是锁对象 @synchronized(self) { // 开始加锁 int count = self.leftTicketCount;
if (count > 0) { [NSThread sleepForTimeInterval:0.05]; self.leftTicketCount = count - 1; NSLog(@"%@卖了?一张票, 剩余%d张票", [NSThread currentThread].name, self.leftTicketCount);
} else { return; // 退出循环
} } // 解锁
} }
通过把自身当做锁对象进行加锁,保证了自己在访问操作数据的时候,别的线程没法进来,只有等待执行完后开锁。
注意:
标签:
原文地址:http://www.cnblogs.com/tommin/p/4663203.html