iOS開發多線程篇—線程安全
阿新 • • 發佈:2019-02-05
object -- nss property 處理 壓力 nat 安全問題 加鎖
一、多線程的安全隱患
資源共享
1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源
比如多個線程訪問同一個對象、同一個變量、同一個文件
當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題
示例一:
示例二:
問題代碼:
1 // 2 // YYViewController.m 3 // 05-線程安全 4 // 5 // Created by apple on 14-6-23. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 10 #import "YYViewController.h" 11 12 @interface YYViewController () 13 //剩余票數 14 15 @property(nonatomic,assign) int leftTicketsCount; 16 @property(nonatomic,strong)NSThread *thread1; 17 @property(nonatomic,strong)NSThread *thread2; 18 @property(nonatomic,strong)NSThread *thread3; 19 20 21 @end 22 23 24 @implementation YYViewController 25 26 27 - (void)viewDidLoad 28 { 29 [super viewDidLoad]; 30 31 //默認有20張票 32 33 self.leftTicketsCount=10; 34 35 //開啟多個線程,模擬售票員售票 36 37 self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil]; 38 39 self.thread1.name=@"售票員A"; 40 41 self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil]; 42 43 self.thread2.name=@"售票員B"; 44 45 self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil]; 46 self.thread3.name=@"售票員C"; 47 } 48 49 50 -(void)sellTickets 51 { 52 while (1) { 53 //1.先檢查票數 54 int count=self.leftTicketsCount; 55 if (count>0) { 56 //暫停一段時間 57 [NSThread sleepForTimeInterval:0.002]; 58 59 //2.票數-1 60 self.leftTicketsCount= count-1; 61 62 //獲取當前線程 63 NSThread *current=[NSThread currentThread]; 64 NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount); 65 }else 66 { 67 //退出線程 68 [NSThread exit]; 69 } 70 } 71 } 72 73 74 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 75 { 76 //開啟線程 77 78 [self.thread1 start]; 79 [self.thread2 start]; 80 [self.thread3 start]; 81 82 } 83 84 @end
打印結果:
二、安全隱患分析
三、如何解決
互斥鎖使用格式
@synchronized(鎖對象) { // 需要鎖定的代碼 }
註意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
代碼示例:
1 // 2 // YYViewController.m 3 // 05-線程安全 4 // 5 // Created by apple on 14-6-23. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h" 10 11 @interface YYViewController () 12 13 //剩余票數 14 @property(nonatomic,assign) int leftTicketsCount; 15 @property(nonatomic,strong)NSThread *thread1; 16 @property(nonatomic,strong)NSThread *thread2; 17 @property(nonatomic,strong)NSThread *thread3; 18 @end 19 20 @implementation YYViewController 21 22 - (void)viewDidLoad 23 { 24 [super viewDidLoad]; 25 //默認有20張票 26 self.leftTicketsCount=10; 27 //開啟多個線程,模擬售票員售票 28 29 self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil]; 30 31 self.thread1.name=@"售票員A"; 32 33 self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil]; 34 35 self.thread2.name=@"售票員B"; 36 37 self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil]; 38 39 self.thread3.name=@"售票員C"; 40 } 41 42 43 -(void)sellTickets 44 { 45 while (1) { 46 @synchronized(self){//只能加一把鎖 47 //1.先檢查票數 48 49 int count=self.leftTicketsCount; 50 if (count>0) { 51 //暫停一段時間 52 [NSThread sleepForTimeInterval:0.002]; 53 //2.票數-1 54 55 self.leftTicketsCount= count-1; 56 //獲取當前線程 57 NSThread *current=[NSThread currentThread]; 58 NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount); 59 60 }else 61 { 62 //退出線程 63 [NSThread exit]; 64 } 65 } 66 } 67 } 68 69 70 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 71 { 72 73 //開啟線程 74 [self.thread1 start]; 75 [self.thread2 start]; 76 [self.thread3 start]; 77 } 78 79 @end
執行效果圖
互斥鎖的優缺點
優點:能有效防止因多線程搶奪資源造成的數據安全問題
缺點:需要消耗大量的CPU資源
互斥鎖的使用前提:多條線程搶奪同一塊資源
相關專業術語:線程同步,多條線程按順序地執行任務
互斥鎖,就是使用了線程同步技術
四:原子和非原子屬性
OC在定義屬性時有nonatomic和atomic兩種選擇
atomic:原子屬性,為setter方法加鎖(默認就是atomic)
nonatomic:非原子屬性,不會為setter方法加鎖
atomic加鎖原理
1 @property (assign, atomic) int age; 2 3 - (void)setAge:(int)age 4 { 5 6 @synchronized(self) { 7 _age = age; 8 } 9 }
原子和非原子屬性的選擇
nonatomic和atomic對比
atomic:線程安全,需要消耗大量的資源
nonatomic:非線程安全,適合內存小的移動設備
iOS開發的建議
所有屬性都聲明為nonatomic
盡量避免多線程搶奪同一塊資源
盡量將加鎖、資源搶奪的業務邏輯交給服務器端處理,減小移動客戶端的壓力
iOS開發多線程篇—線程安全