標籤:a star 原因分析 status self ams eve sign selector nsdate
一個NSThread對象就代表一條線程 下面是NSThread開啟線程的方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self openThreadWithNSThread]; [NSThread mainThread];//擷取主線程 [NSThread currentThread]; //擷取當前線程}- (void) openThreadWithNSThread { /* *第一個參數 目標對象 self *第二個參數 方法選取器 調用的方法 *第三個參數 前面調用方法需要傳遞的參數 可以為nil */ //第一種方式 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:@"ABC"]; thread.name = @"線程1"; //0.0-1.0之間 1.0最高 0.0最低 預設0.5 越高線程被調用的頻率越大 thread.threadPriority = 1.0; [thread start];//需要手動調用啟動線程 //第二種方式 [NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:@"abc2"];//自動啟動線程 //第三種方式 [self performSelectorInBackground:@selector(threadAction:) withObject:@"開啟一條後台線程"];//開啟一條後台線程 //block方式 NSThread *thread1 = [[NSThread alloc] initWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; thread1.name = @"線程2"; thread1.threadPriority = 0.5; [thread start]; //或者 [NSThread detachNewThreadWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; }- (void) threadAction:(NSString *)params { NSLog(@"%@ and %@",params,[NSThread currentThread].name);}
NSThread 建立線程的生命週期
當線程中的任務執行完畢後 線程被釋放掉 可以繼承NSThread建立一個新類 重寫dealloc方法來驗證
線程的狀態
當線程處於就緒狀態時線程會被移到可調度線程池裡面(CPU只調度此線程池裡面的線程),當處於阻塞狀態時,線程會被移出可調度線程池,當處於死亡狀態時 先移出線程池,再從記憶體中釋放。
- (void)threadStatus { //建立線程 在記憶體中建立一個線程 (建立狀態) NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction1) object:nil]; [thread start]; //就緒狀態 會被放倒可調度線程池裡面 只有在可調度線程池裡面的線程才是可以被CPU調度的 }//一旦線程死亡了 就不能再次開啟任務- (void)threadAction1 { //運行狀態 for (NSInteger i = 0; i < 10000; i ++) { if (i == 5000) { //[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; [NSThread sleepForTimeInterval:2.0];//阻塞狀態 移出線程池 //兩秒鐘過後 再次進入就緒狀態 等待調度進入運行狀態 } if (i == 8000) { //break;//任務完成 自然死亡 [NSThread exit]; //廢除線程強制進入死亡狀態 } NSLog(@"%@",[NSThread currentThread]); } //執行完方法之後死亡狀態}
線程的安全 也非常重要 這裡介紹一種方法 後面會著重介紹
多線程的安全隱患
資源共用
1塊資源可能會被多個線程共用,也就是多個線程可能會訪問同一塊資源 比如多個線程訪問同一個對象 同一個變數 同一個檔案 此時就很容易引發資料錯亂和資料安全問題.
安全隱患原因分析
安全隱患的解決
問題代碼
#import "ViewController.h"@interface ViewController ()@property (nonatomic,strong) NSThread *threadA;//售票員A@property (nonatomic,strong) NSThread *threadB;//售票員B@property (nonatomic,strong) NSThread *threadC;//售票員C@property (nonatomic,assign) NSInteger totalCount;@end@implementation ViewController- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //設定總票數 self.totalCount = 1000; self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadA.name = @"售票員A"; [self.threadA start]; self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadB.name = @"售票員B"; [self.threadB start]; self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadC.name = @"售票員C"; [self.threadC start];}//售票- (void)saleTicket { while (1) { NSInteger count = self.totalCount; if (count > 0) { for (NSInteger i = 0; i < 100000; i ++) { } //賣出去一張票 self.totalCount = count - 1; NSLog(@"%@ 賣出去一張票 還剩%zd張票",[NSThread currentThread].name,self.totalCount); }else { NSLog(@"票賣完了"); break; } } }
列印結果
2018-03-05 22:28:38.600491+0800 NSThreadDemo[1016:86284] 售票員A 賣出去一張票 還剩999張票2018-03-05 22:28:38.600493+0800 NSThreadDemo[1016:86285] 售票員B 賣出去一張票 還剩999張票2018-03-05 22:28:38.600519+0800 NSThreadDemo[1016:86286] 售票員C 賣出去一張票 還剩999張票
問題解決代碼
#import "ViewController.h"@interface ViewController ()@property (nonatomic,strong) NSThread *threadA;//售票員A@property (nonatomic,strong) NSThread *threadB;//售票員B@property (nonatomic,strong) NSThread *threadC;//售票員C@property (nonatomic,assign) NSInteger totalCount;@end@implementation ViewController- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //設定總票數 self.totalCount = 1000; self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadA.name = @"售票員A"; [self.threadA start]; self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadB.name = @"售票員B"; [self.threadB start]; self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadC.name = @"售票員C"; [self.threadC start];}//售票- (void)saleTicket { while (1) { //鎖必須是全域唯一的 @synchronized(self) { NSInteger count = self.totalCount; if (count > 0) { for (NSInteger i = 0; i < 100000; i ++) { } //賣出去一張票 self.totalCount = count - 1; NSLog(@"%@ 賣出去一張票 還剩%zd張票",[NSThread currentThread].name,self.totalCount); }else { NSLog(@"票賣完了"); break; } } } }
互斥鎖的優缺點
優點:能有效防止因多線程搶奪資源造成的資料安全問題
缺點:需要消耗大量的CPU資源
互斥鎖的使用前提:多條線程搶奪同一塊資源
相關專業術語:線程同步,多條線程按順序地執行任務
互斥鎖,就是使用了線程同步技術
原子和非原子屬性
OC在定義屬性時有nonatomic和atomic兩種選擇
atomic:原子屬性,為setter方法加鎖(預設就是atomic)
nonatomic:非原子屬性,不會為setter方法加鎖
1 @property (assign, atomic) int age;2 3 - (void)setAge:(int)age4 { 5 6 @synchronized(self) { 7 _age = age;8 }9}
原子和非原子屬性的選擇
nonatomic和atomic對比
atomic:安全執行緒,需要消耗大量的資源(並非是真正的安全執行緒 更準確的說應該是讀寫安全,但並不是安全執行緒的,因為別的線程還能進行讀寫之外的其他動作。安全執行緒需要開發人員自己來保證。)
nonatomic:非安全執行緒,適合記憶體小的行動裝置
iOS開發的建議
所有屬性都聲明為nonatomic
盡量避免多線程搶奪同一塊資源
盡量將加鎖、資源搶奪的商務邏輯交給伺服器端處理,減小移動用戶端的壓力
iOS 多線程之 NSThread的基本使用