多線程安全:
1塊資源可能會(huì)被多個(gè)線程共享,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源,比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象、同一個(gè)變量、同一個(gè)文件。當(dāng)多個(gè)線程訪問(wèn)同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題。
舉例1:一個(gè)可變數(shù)組?NSMutableArray *marray,數(shù)組里面有5個(gè)元素;當(dāng)兩個(gè)線程同時(shí)讀取到marray的count為1時(shí),都執(zhí)行remove操作數(shù)組會(huì)崩潰(注意不可能會(huì)同時(shí)訪問(wèn),只是兩次讀取操作訪問(wèn)的時(shí)間間隔非常逼近)
舉例2:比如對(duì)于@property(nonatomic,copy)NSString *str; 當(dāng)調(diào)用self.str = @"HELLO,GUY";如果是多線程,在一個(gè)線程執(zhí)行setter方法的時(shí)候,會(huì)涉及到字符串拷貝,另一個(gè)線程去讀取,很可能讀到一半的數(shù)據(jù),也就是garbage數(shù)據(jù)。
舉例3:


當(dāng)存錢1000和取錢1000這個(gè)過(guò)程幾乎同時(shí)發(fā)生,他們讀取余額都是1000,但是存錢過(guò)程執(zhí)行1000+1000這時(shí)余額變?yōu)?000,而隨后取錢過(guò)程執(zhí)行1000-500這時(shí)余額變成500;由于取錢過(guò)程稍晚于存錢,所以最終余額變?yōu)?00;這樣由于多線程問(wèn)題余額顯示異常
賣票問(wèn)題代碼:
?//剩余票數(shù)
?@property(atomic,assign) int leftTicketsCount;
?@property(nonatomic,strong)NSThread *thread1;
?@property(nonatomic,strong)NSThread *thread2;
?@property(nonatomic,strong)NSThread *thread3;
?????????//默認(rèn)有100張票
? ? ? ? ?self.leftTicketsCount=100;
? ? ? ? ?//開(kāi)啟多個(gè)線程,模擬售票員售票
? ? ? ? ?self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
?? ? ? ? self.thread1.name=@"售票員A";
? ? ? ? ? self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
? ? ? ? ? self.thread2.name=@"售票員B";
?? ? ? ? self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
?? ? ? ? self.thread3.name=@"售票員C";
-(void)sellTickets {
?? ? ? ? while(1) {
?? ? ? ? ? ? ? ? //1.先檢查票數(shù)
?? ? ? ? ? ? ? ? intcount=self.leftTicketsCount;
?? ? ? ? ? ? ? ? if(count>0) {
?? ? ? ? ? ? ? ? ? ? ? ? //暫停一段時(shí)間
?? ? ? ? ? ? ? ? ? ? ? ? [NSThreadsleepForTimeInterval:0.002];
?? ? ? ? ? ? ? ? ? ? ? ? //2.票數(shù)-1
? ? ? ? ? ? ? ? ? ? ? ? self.leftTicketsCount= count-1;
?? ? ? ? ? ? ? ? ? ? ? ? //獲取當(dāng)前線程
?? ? ? ? ? ? ? ? ? ? ? ? NSThread*current=[NSThreadcurrentThread];
?? ? ? ? ? ? ? ? ? ? ? ? NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount);
?? ? ? ? ? ? ? ? ? ? }else{
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //退出線程
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [NSThread exit];
?? ? ? ? ? ? ? ? ? ? ? ? ? ? }
?? ? ? ? ? ? }
}
會(huì)出現(xiàn)同時(shí)讀取數(shù)據(jù)的情況,A和B同時(shí)讀到票剩余50張,同時(shí)執(zhí)行票減一行為,此時(shí)實(shí)際剩余票數(shù)應(yīng)為:50-2
;而因?yàn)樽x到的都是50,最后結(jié)果變?yōu)?9
使用互斥鎖@synchronized(self){}保護(hù)self對(duì)象,在{}作用域內(nèi)防止self對(duì)象在同一時(shí)間內(nèi)被其它線程訪問(wèn)
sell tickets修改為:
? //默認(rèn)有20張票
?? ? ? ? self.leftTicketsCount=100;
?? ? ? ? //開(kāi)啟多個(gè)線程,模擬售票員售票
?? ? ? ? self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
?? ? ? ? self.thread1.name=@"售票員A";
? ? ? ? ? self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets2) object:nil];
? ? ? ? ? self.thread2.name=@"售票員B";
?? ? ? ? self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets3) object:nil];
?? ? ? ? self.thread3.name=@"售票員C";
-(void)sellTickets
?{
? ? ? @synchronized(self){
?? ? ? ? while(1) {
?? ? ? ? ? ? ? ? //1.先檢查票數(shù)
?? ? ? ? ? ? ? ? intcount=self.leftTicketsCount;
?? ? ? ? ? ? ? ? if(count>0) {
?? ? ? ? ? ? ? ? ? ? ? ? //暫停一段時(shí)間
?? ? ? ? ? ? ? ? ? ? ? ? [NSThreadsleepForTimeInterval:0.002];
?? ? ? ? ? ? ? ? ? ? ? ? //2.票數(shù)-1
? ? ? ? ? ? ? ? ? ? ? ? self.leftTicketsCount= count-1;
?? ? ? ? ? ? ? ? ? ? ? ? //獲取當(dāng)前線程
?? ? ? ? ? ? ? ? ? ? ? ? NSThread*current=[NSThreadcurrentThread];
?? ? ? ? ? ? ? ? ? ? ? ? NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount);
?? ? ? ? ? ? ? ? ? ? }else
?? ? ? ? ? ? ? ? ? ? ? ? {
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //退出線程
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [NSThreadexit];
?? ? ? ? ? ? ? ? ? ? ? ? ? ? }
?? ? ? ? ? ? }
? ? ? }
}