當(dāng)你想在線程中執(zhí)行一個(gè)很長的任務(wù),但又不希望它阻塞應(yīng)用程序其余部分的執(zhí)行時(shí),子線程尤其有用,你可以將任務(wù)放在子線程中來執(zhí)行,避免阻塞應(yīng)用程序的主線程,讓主線程處理用戶界面和與事件相關(guān)的操作,子線程用于將大型任務(wù)劃分為幾個(gè)較小的任務(wù),這能會(huì)使多核計(jì)算機(jī)上的性能提高。
NSThread使用
- 獲取主線程&當(dāng)前線程
// 獲取主線程 NSThread *mainThred = [NSThread mainThread]; // 獲取當(dāng)前線程 NSThread *currentThred = [NSThread currentThread]; - 線程的創(chuàng)建
- 這種創(chuàng)建方式需要手動(dòng)啟動(dòng)線程
- (void)viewDidLoad {
[super viewDidLoad];
// 創(chuàng)建一條線程(方式一)
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:@"我是一條線程"];
// 啟動(dòng)線程
[thread start];
}
- 以下幾種方式創(chuàng)建線程會(huì)自動(dòng)啟動(dòng)線程,但是出了方式二以外的幾種創(chuàng)建方式都無法對(duì)線程進(jìn)行詳細(xì)的設(shè)置。
// 方式二
NSThread *thread = [[NSThread alloc] initWithBlock:^{
}];
// 方式三
[NSThread detachNewThreadWithBlock:^{
NSLog(@"thread=%@-------",[NSThread currentThread]);
}];
// 方式四
[NSThread detachNewThreadSelector:@selector(runThread:) toTarget:self withObject:@"線程"];
// 方式五
[self performSelectorInBackground:@selector(runThread:) withObject:@"線程"];
- 常用屬性
// 優(yōu)先級(jí) 0.0 ~ 1.0 默認(rèn) 0.5 數(shù)值越大優(yōu)先級(jí)越高
@property double threadPriority ;
/** NSQualityOfService:
NSQualityOfServiceUserInteractive:最高優(yōu)先級(jí),主要用于提供交互UI的操作,比如處理點(diǎn)擊事件,繪制圖像到屏幕上
NSQualityOfServiceUserInitiated:次高優(yōu)先級(jí),主要用于執(zhí)行需要立即返回的任務(wù)
NSQualityOfServiceDefault:默認(rèn)優(yōu)先級(jí),當(dāng)沒有設(shè)置優(yōu)先級(jí)的時(shí)候,線程默認(rèn)優(yōu)先級(jí)
NSQualityOfServiceUtility:普通優(yōu)先級(jí),主要用于不需要立即返回的任務(wù)
NSQualityOfServiceBackground:后臺(tái)優(yōu)先級(jí),用于完全不緊急的任務(wù)
*/
@property NSQualityOfService qualityOfService;
// 線程名稱(區(qū)別多條線程)
@property (nullable, copy) NSString *name;
// 線程正在執(zhí)行
@property (readonly, getter=isExecuting) BOOL executing;
// 線程執(zhí)行結(jié)束
@property (readonly, getter=isFinished) BOOL finished;
// 線程是否可以取消
@property (readonly, getter=isCancelled) BOOL cancelled;
- 常用方法
-(void)start; // 啟動(dòng)線程
-(BOOL)isMainThread; // 是否為主線程
-(void)setName:(NSString *)n; // 設(shè)置線程名稱
-(void)cancel ;// 取消線程
-(void)isExecuting; // 判斷線程是否正在執(zhí)行
-(void)isCancelled; // 判斷線程是否撤銷
-(void)isFinished; // 判斷線程是否已經(jīng)結(jié)束
+(void)currentThread; // 獲取當(dāng)前線程
+(BOOL)isMultiThreaded; // 當(dāng)前代碼運(yùn)行所在線程是否是子線程
+(void)sleepUntilDate:(NSDate *)date; //當(dāng)前代碼所在線程睡到指定時(shí)間
+(void)sleepForTimeInterval:(NSTimeInterval)ti; //當(dāng)前線程睡多長
時(shí)間
+(void)exit; // 退出當(dāng)前線程
- 線程的生命周期
當(dāng)線程中的任務(wù)執(zhí)行完成過后被釋放
- 線程的狀態(tài)
- 就緒狀態(tài):當(dāng)前線程準(zhǔn)備就緒,CPU調(diào)度當(dāng)前線程--->變?yōu)檫\(yùn)行狀態(tài)。
- 運(yùn)行狀態(tài):當(dāng)前線程運(yùn)行中,當(dāng)CPU調(diào)度其他線程--->變?yōu)榫途w狀態(tài)。
- 阻塞狀態(tài):當(dāng)前線程調(diào)用sleep方法,等睡眠時(shí)間結(jié)束--->變?yōu)榫途w狀態(tài)。
- 死亡狀態(tài):線程任務(wù)執(zhí)行完成,移出可調(diào)度線程池,被釋放掉。
線程的安全
-
如果多個(gè)線程(同時(shí))訪問同一資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題。
線程安全.png
總共有100張電影票,當(dāng)售票員01和02同時(shí)獲取到余票為一百張時(shí),01賣出了80張余票為20張,02賣出了21張余票為79張,實(shí)際上他們賣出了101張但是總票數(shù)只有100張,這樣就多出一個(gè)人沒法看電影,這就是引發(fā)數(shù)據(jù)錯(cuò)亂。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,strong) NSThread *threadA;
@property (nonatomic,strong) NSThread *threadB;
@property (nonatomic,strong) NSThread *threadC;
@property (nonatomic,assign) NSInteger totalCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 總共設(shè)置100 票
self.totalCount = 100;
self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
self.threadA.name = @"售票員A";
self.threadB.name = @"售票員B";
self.threadC.name = @"售票員C";
[self.threadA start];
[self.threadB start];
[self.threadC start];
}
-(void)runThread:(id)obj{
while (1) {
NSInteger count = self.totalCount;
if (count>0) {
for (int i = 0; i<1000; i++) { // 模擬售票時(shí)間
self.totalCount = count - 1;
}
NSLog(@"%@賣了一張票還剩%zd",[NSThread currentThread].name,self.totalCount);
}else{
NSLog(@"售完了");
break;
}
}
}
@end

輸出結(jié)果.png
- 互斥鎖 「@synchronized () { }」
- 鎖是必須全局唯一的
- 注意加鎖的位置。
- 注意加鎖條件(多線程訪問同一資源)
- 加鎖的結(jié)果會(huì)導(dǎo)致線程同步(多條線程在同一條線上執(zhí)行,按順序執(zhí)行任務(wù))
-(void)runThread:(id)obj{
while (1) {
// 鎖:必須是全局唯一的
@synchronized (self) { // 互斥鎖
NSInteger count = self.totalCount;
if (count>0) {
for (int i = 0; i<1000; i++) { // 模擬售票時(shí)間
self.totalCount = count - 1;
}
NSLog(@"%@賣了一張票還剩%zd",[NSThread currentThread].name,self.totalCount);
}else{
NSLog(@"售完了");
break;
}
}
}
}

互斥鎖結(jié)果.png
非原子屬&性原子
- nonatomic:非原子屬性,不會(huì)為setter方法加鎖,非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備。
- atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic),線程安全,需要耗費(fèi)大量的資源。
線程間通信
- 一個(gè)線程傳遞數(shù)據(jù)給另一個(gè)線程
- 在一個(gè)線程中執(zhí)行完成任務(wù),轉(zhuǎn)到另一個(gè)線程中繼續(xù)執(zhí)行任務(wù)(列如子線程下載圖片,主線程顯示圖片)
- 常用方法
// 從當(dāng)前線程切換到主線程中
-(void)performSelectorOnMainThread:(SEL)aSelector
withObject:(id)arg
waitUntilDone:(BOOL)wait;
// 從當(dāng)前線程切換到指定的線程
-(void)performSelector:(SEL)aSelector
onThread:(NSThread *)thr
withObject:(id)arg
waitUntilDone:(BOOL)wait;
