假如我們在功能的實現(xiàn)過程中眨层,類中有一個全局變量锁右,我們創(chuàng)建了多個線程去同時改變或者使用這個變量,會出現(xiàn)什么問題很魂?
線程鎖就是用來解決多線程之間對資源共享的問題扎酷;
思路
在上文《多線程之三》的基礎(chǔ)之上進(jìn)行演示,模仿多個地點進(jìn)行售票的案例遏匆。
代碼展示
1:創(chuàng)建按鈕
//問題 : 當(dāng)多個線程執(zhí)行某一塊相同代碼法挨,需要線程鎖進(jìn)行保護(hù)
UIButton *btn3 = [UIButton buttonWithType:UIButtonTypeCustom];
btn3.frame = CGRectMake(40, 200, 100, 40);
[btn3 setTitle:@"線程鎖" forState:UIControlStateNormal];
[btn3 setBackgroundColor:[UIColor blueColor]];
[btn3 addTarget:self action:@selector(click_lock) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn3];
2:創(chuàng)建售票的管理對象
TicketManager.h
@interface TicketManager : NSObject
- (void) startSale ;
@end
TicketManager.m
首先做一個擴(kuò)展
\#define Total 100 //一共一百張票
@interface TicketManager ()
@property (nonatomic, assign) int whole; //總票數(shù)
@property (nonatomic, assign) int surplus; //剩余票數(shù)
@property (nonatomic, strong) NSThread *thread_SH; //子線程, 上海售票點
@property (nonatomic, strong) NSThread *thread_BJ; //子線程幅聘, 北京售票點
@property (nonatomic, strong) NSThread *thread_SZ; //子線程凡纳, 深圳售票點
@end
初始化售賣點
@implementation TicketManager
- (instancetype)init
{
self = [super init];
if (self) {
self.whole = Total;
self.surplus = Total;
self.thread_SH = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
self.thread_SH.name = @"上海售票點";
self.thread_BJ = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
self.thread_BJ.name = @"北京售票點";
self.thread_SZ = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
self.thread_SZ.name = @"深圳售票點";
}
return self;
}
實現(xiàn)售賣方法
//售票方法
- (void) sale {
while (true) {
if (self.surplus > 0) { //當(dāng)還有余票時,就執(zhí)行賣票
[NSThread sleepForTimeInterval:1];
self.surplus = self.surplus - 1;
NSLog(@"%@ 賣出一張票帝蒿,剩余:%d",[NSThread currentThread].name, self.surplus);
}
}
}
三個地方開始同時售票
//開始賣票
- (void) startSale {
[self.thread_SZ start];
[self.thread_BJ start];
[self.thread_SH start];
}
3:在 viewController 里面創(chuàng)建和使用TicketMananger
@property (nonatomic, strong) TicketManager *ticketManager; //票務(wù)管理
在 viewDidLoad 中初始化
self.ticketManager = [[TicketManager alloc] init];
在 click_lock 中啟動賣票
NSLog(@"開始賣票");
[self.ticketManager startSale];
4:開始執(zhí)行荐糜,查看打印結(jié)果
根據(jù)日志分析:非常明顯剩余票數(shù)不正確。
接下來我們引入鎖的概念來解決這個問題陵叽。簡單說明下鎖是什么概念呢狞尔?咱們可以先這樣理解:當(dāng)我在獨自使用一個房間的時候丛版,不希望別人同時使用和打擾巩掺,這樣呢,我們也就可以先對該房間加上鎖页畦,然后再使用胖替,在使用完成之后,該房間不歸我使用了,我就解開鎖独令,以方便其他人使用端朵、
線程鎖有三種方式:
1 : @synchronized
使用方法:將要保護(hù)起來的代碼塊添加到 @synchronized 的括號中包裹起來
//線程鎖的第一種方式 :@synchronized
@synchronized (self) {
if (self.surplus > 0) { //當(dāng)還有余票時,就執(zhí)行賣票
[NSThread sleepForTimeInterval:1];
self.surplus = self.surplus - 1;
NSLog(@"%@ 賣出一張票燃箭,剩余:%d",[NSThread currentThread].name, self.surplus);
}
}
2:NSCondition
使用方法:創(chuàng)建了 NSCondition 對象冲呢,將保護(hù)起來的代碼塊使用 lock 和 unlock 進(jìn)行前后包裹;
//線程鎖的第二種方式:NSCondition
if (!self.condition) {
self.condition = [[NSCondition alloc] init];
}
[self.condition lock];
if (self.surplus > 0) { //當(dāng)還有余票時招狸,就執(zhí)行賣票
[NSThread sleepForTimeInterval:1];
self.surplus = self.surplus - 1;
NSLog(@"%@ 賣出一張票敬拓,剩余:%d",[NSThread currentThread].name, self.surplus);
}
[self.condition unlock];
3:NSLock
使用方法:創(chuàng)建了 NSLock 對象,將保護(hù)起來的代碼塊使用 lock 和 unlock 進(jìn)行前后包裹裙戏;
//線程鎖的第三種方式:NSLock
if (!self.lock) {
self.lock = [[NSLock alloc] init];
}
[self.lock lock];
if (self.surplus > 0) { //當(dāng)還有余票時乘凸,就執(zhí)行賣票
[NSThread sleepForTimeInterval:1];
self.surplus = self.surplus - 1;
NSLog(@"%@ 賣出一張票,剩余:%d",[NSThread currentThread].name, self.surplus);
}
[self.lock unlock];
在我們使用了以上三種線程鎖之后的打印結(jié)果是怎么樣的呢累榜?
可以看出打印的結(jié)果是健康和良性的营勤;
至此,線程鎖的使用先告一段落壹罚,我會再后期詳細(xì)給出三種線程鎖的詳細(xì)特性和使用場景葛作。敬請期待 ~~~~