上篇文章中講到了多線程訪問共享資源時,會產生數據錯亂,解決這個問題的方法就是,通過互斥鎖保護公共資源
互斥鎖:有效防止因多線程搶奪資源造成的數據安全問題(線程同步技術) :
@synchronized(鎖對象){ 鎖定代碼 }
當一條線程訪問通過互斥鎖訪問公共資源時,進行加鎖保護,當其他線程執行到這里,需要訪問公共資源時,會在鎖外等候,當前一線程執行完互斥鎖內的任務,會通知鎖外等候的線程,這樣后面的線程再開啟鎖保護,執行鎖內的任務
實現同一時間 , 只有一條線程可以對公共資源進行讀寫操作
示例代碼:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
NSInteger _totalCounts;
}
- (void)viewDidLoad {
[super viewDidLoad];
_totalCounts = 10;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
[thread1 start];
[thread2 start];
}
- (void)demo{
/*
互斥鎖(線程同步) :
@synchronized(鎖對象){
鎖定代碼
}
作為鎖對象的條件:
1: 繼承自NSObject
2: 必須是全局的
*/
@synchronized (self) {
for (int i = 0; i < 10 ;i ++) {
if (_totalCounts > 0) {
_totalCounts--;
NSLog(@"%zd",_totalCounts);
}else{
NSLog(@"停止");
break;
}
}
}
}
@end
作為鎖對象的兩個條件:
1: 繼承自NSObject
2: 必須是全局的
如果不滿足全局,就不能保證鎖的唯一性,相當于入口不唯一:
當一條線程訪問公共資源前進行了加鎖,另外一條線程訪問公共資源前也加了鎖,兩個線程從不同的入口進入,獲取到了公共資源,這樣也就失去了意義
而之所以使用self,是因為self是最容易獲取到的2個條件都滿足的全局鎖對象
- __互斥鎖的原理 : __
1: 默認繼承自NSObject的對象內部都有一把互斥鎖,默認開啟狀態
2: 當執行到@synchronized關鍵字時,會先檢查對象的鎖的狀態是開啟還是關閉,通過鎖對象內部的鎖,鎖住大括號內的代碼,再去執行大括號內的代碼,這樣其他線程就被擋在鎖外等候,當進入互斥鎖內部的線程執行完鎖內代碼后,會重新打開互斥鎖,這樣在鎖外等候的線程就可以進來了,重復加鎖-->執行內部代碼-->解鎖的操作
加鎖后的程序執行效率比不加鎖的時候要低,因為線程需要等待鎖
但是鎖保證了多個線程同時操作公共資源的安全性