互斥鎖/線程同步(@synchronized(對(duì)象))
- 用途:為了解決一塊資源被多個(gè)線程共享造成的數(shù)據(jù)紊亂和數(shù)據(jù)安全問題
- 只要被@synchronized的{}包裹起來的代碼荔棉,同一時(shí)刻就只能被一個(gè)線程執(zhí)行
- 注意:
- 只要加鎖就會(huì)消耗性能
- 加鎖必須傳遞一個(gè)對(duì)象,作為鎖(一般都是傳self)
- 如果想真正的鎖住代碼,那么多個(gè)線程必須使用同一把鎖才行
- 加鎖的時(shí)候盡量縮小范圍堵泽,因?yàn)榉秶酱笮阅芫驮降?/li>
多線程的實(shí)現(xiàn)方案
1.Pthread(C):基本不用
2.NSThread(OC):可拿到線程對(duì)象桐绒,設(shè)置線程的一些屬性降淮,結(jié)束線程等
3.GCD(C):用的最多乞旦,使用最簡(jiǎn)單(不能拿到線程续膳,開始了就很難手動(dòng)結(jié)束)
4.NSOperation(OC):抽象類,要使用其子類
Pthread
// 第一個(gè)參數(shù): 線程的代號(hào)(當(dāng)做就是線程)
// 第二個(gè)參數(shù): 線程的屬性
// 第三個(gè)參數(shù): 指向函數(shù)的指針, 就是將來線程需要執(zhí)行的方法
// 第四個(gè)參數(shù): 給第三個(gè)參數(shù)的指向函數(shù)的指針 傳遞的參數(shù)
pthread_t threadId;
// 只要create一次就會(huì)創(chuàng)建一個(gè)新的線程
pthread_create(&threadId , NULL, &demo, "lnj");
NSThread
注意:如果正在執(zhí)行系統(tǒng)分離出來的線程(子線程)時(shí)胖替,系統(tǒng)內(nèi)部會(huì)retain當(dāng)前線程研儒,只有線程中的方法執(zhí)行完畢,系統(tǒng)才會(huì)將其釋放(release)
創(chuàng)建方式一(可拿到子線程對(duì)象設(shè)置一些屬性独令,需要手動(dòng)開啟子線程)
// 創(chuàng)建子線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 設(shè)置線程的其他屬性
thread.name = @"second";
// 開啟子線程
[thread start];
- 創(chuàng)建方式二(不能拿到子線程對(duì)象端朵,不需要手動(dòng)開啟子線程,快速)
// 創(chuàng)建子線程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
```
- 創(chuàng)建方式三(系統(tǒng)自動(dòng)創(chuàng)建子線程并在self的@selector方法中執(zhí)行燃箭,**快速**)
```objc
// 第三種創(chuàng)建方式
[self performSelectorInBackground:@selector(run) withObject:nil];
- 常用方法
+ (NSThread *)mainThread; // 獲得主線程
- (BOOL)isMainThread; // 是否為主線程
+ (BOOL)isMainThread; // 是否為主線程
// 獲取當(dāng)前線程(最常用)
[NSThread currentThread];
// 線程的調(diào)度優(yōu)先級(jí)冲呢,調(diào)度優(yōu)先級(jí)的取值范圍是0.0 ~ 1.0,默認(rèn)0.5遍膜,值越大碗硬,優(yōu)先級(jí)越高
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority;
- (BOOL)setThreadPriority:(double)p;
// 線程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
// 阻塞(暫停)線程,**如果想實(shí)現(xiàn)延長啟動(dòng)圖片的顯示時(shí)間可在application的didFinish方法中用此方法讓線程睡一會(huì)**
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 強(qiáng)制停止線程
+ (void)exit;
GCD
優(yōu)勢(shì)
1.GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案
2.GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核瓢颅、四核)
3.GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程恩尾、調(diào)度任務(wù)、銷毀線程)
4.程序員只需要告訴GCD想要執(zhí)行什么任務(wù)挽懦,不需要編寫任何線程管理代碼-
GCD中有2個(gè)核心概念
- 任務(wù):執(zhí)行什么操作
- 隊(duì)列:用來存放任務(wù)
GCD的使用就2步
1.定制任務(wù)(確定要執(zhí)行什么)
2.將任務(wù)添加到隊(duì)列(GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出翰意,放到對(duì)應(yīng)的線程中執(zhí)行,任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出信柿,后進(jìn)后出)-
任務(wù)執(zhí)行(GCD中有2個(gè)用來執(zhí)行任務(wù)的常用函數(shù))
- 同步:只能在當(dāng)前線程中執(zhí)行任務(wù)冀偶,不具備開啟新線程的能力
// queue:隊(duì)列
// block:任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
-
隊(duì)列類型
- 串行:讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后渔嚷,再執(zhí)行下一個(gè)任務(wù))
// 串行隊(duì)列的創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
// 兩個(gè)參數(shù)分別是:隊(duì)列的名稱,隊(duì)列的類型
// 創(chuàng)建串行隊(duì)列(隊(duì)列類型傳遞NULL或者DISPATCH_QUEUE_SERIAL)
- 并發(fā):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
// 并發(fā)隊(duì)列的創(chuàng)建进鸠,兩個(gè)參數(shù)分別是:隊(duì)列的名稱,隊(duì)列的類型
dispatch_queue_t queue = dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
- 獲取全局并發(fā)隊(duì)列
// GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用形病,可以無需手動(dòng)創(chuàng)建
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 第一個(gè)參數(shù)是優(yōu)先級(jí)客年,傳0即可霞幅,第二參數(shù)暫時(shí)無用,傳0
- 全局并發(fā)隊(duì)列的優(yōu)先級(jí)
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(rèn)(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺(tái)
- 獲取主隊(duì)列(**放在主隊(duì)列中的任務(wù)量瓜,都會(huì)放到主線程中執(zhí)行**)
// 主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
- *注意:使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù)司恳,會(huì)卡住當(dāng)前的串行隊(duì)列*
- 任務(wù)函數(shù)和隊(duì)列類型的搭配使用
/*
同步 + 主隊(duì)列 = 需要記住的就一點(diǎn): 同步函數(shù)不能搭配主隊(duì)列使用
注意: 如果是在子線程中調(diào)用同步函數(shù) + 主對(duì)列 是可以執(zhí)行的
*/
- (void)syncMian
{
// 主隊(duì)列, 只要將任務(wù)放到主隊(duì)列中, 那么任務(wù)就會(huì)在主線程中執(zhí)行
dispatch_queue_t queue = dispatch_get_main_queue();
// 需要記住的就一點(diǎn): 同步函數(shù)不能搭配主隊(duì)列使用
dispatch_sync(queue, ^{
NSLog(@"1 - %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2 - %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3 - %@", [NSThread currentThread]);
});
NSLog(@"++++++++++++++");
}
/*
異步 + 主隊(duì)列 = 不會(huì)開啟新的線程
*/
- (void)asyncMain
{
// 主隊(duì)列, 只要將任務(wù)放到主隊(duì)列中, 那么任務(wù)就會(huì)在主線程中執(zhí)行
dispatch_queue_t queue = dispatch_get_main_queue();
// 如果任務(wù)放在主隊(duì)列中, 哪怕是異步方法也不會(huì)創(chuàng)建新的線程
dispatch_async(queue, ^{
NSLog(@"1 - %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2 - %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3 - %@", [NSThread currentThread]);
});
}
/*
同步 + 串行 = 不會(huì)創(chuàng)建新的線程
注意: 如果是同步函數(shù), 只要代碼執(zhí)行到了同步函數(shù)的那一行, 就會(huì)立即執(zhí)行任務(wù), 只有任務(wù)執(zhí)行完畢才會(huì)繼續(xù)往后執(zhí)行
*/
- (void)syncSerial
{
// 1.創(chuàng)建隊(duì)列
/*
正是因?yàn)榫€程默認(rèn)就是串行, 所以創(chuàng)建串行隊(duì)列的時(shí)候, 隊(duì)列類型可以不傳值
*/
// dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", NULL);
// 2.添加任務(wù)
dispatch_sync(queue, ^{
NSLog(@"1 - %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2 - %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3 - %@", [NSThread currentThread]);
});
NSLog(@"%s", __func__);
}
/*
同步 + 并行 = 不會(huì)開啟新的線程
注意: 能不能開啟新的線程, 和并行/串行沒有關(guān)系, 只要函數(shù)是同步還是異步
*/
- (void)syncConcurrent
{
// 1.創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.添加任務(wù)
dispatch_sync(queue, ^{
NSLog(@"1 - %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2 - %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3 - %@", [NSThread currentThread]);
});
NSLog(@"%s", __func__);
}
/*
異步 + 串行 = 會(huì)創(chuàng)建新的線程, 但是只會(huì)創(chuàng)建一個(gè)新的線程, 所有的任務(wù)都在這一個(gè)新的線程中執(zhí)行
異步任務(wù), 會(huì)先執(zhí)行完所有的代碼, 再在子線程中執(zhí)行任務(wù)
*/
- (void)asyncSerial
{
// 1.創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_SERIAL);
// 2.添加任務(wù)
dispatch_async(queue, ^{
NSLog(@"1 - %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2 - %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3 - %@", [NSThread currentThread]);
});
NSLog(@"%s", __func__);
}
/*
異步 + 并行 = 會(huì)開啟新的線程
*/
- (void)asynConcurrent
{
/*
第一個(gè)參數(shù): 隊(duì)列
第二個(gè)參數(shù): 任務(wù)
*/
// 1.創(chuàng)建隊(duì)列
/*
第一個(gè)參數(shù): 隊(duì)列的名稱
第二個(gè)參數(shù): 隊(duì)列的類型
*/
// dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
// 其實(shí)系統(tǒng)內(nèi)部已經(jīng)給我們提供了一個(gè)現(xiàn)成的并發(fā)隊(duì)列
/*
第一個(gè)參數(shù): iOS8以前是線程的優(yōu)先級(jí)/ iOS8以后代表服務(wù)質(zhì)量
iOS8以前
* - DISPATCH_QUEUE_PRIORITY_HIGH: 2
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: 0
* - DISPATCH_QUEUE_PRIORITY_LOW: -2
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND: -32768
iOS8開始, 取值都是十六進(jìn)制
* - QOS_CLASS_USER_INTERACTIVE 用戶交互(用戶迫切的想執(zhí)行任務(wù), 不要在這種服務(wù)質(zhì)量下做耗時(shí)的操作)
* - QOS_CLASS_USER_INITIATED 用戶需要
* - QOS_CLASS_DEFAULT 默認(rèn)(重置隊(duì)列)
* - QOS_CLASS_UTILITY 實(shí)用工具(耗時(shí)的操作放在這里)
* - QOS_CLASS_BACKGROUND
* - QOS_CLASS_UNSPECIFIED 沒有設(shè)置任何優(yōu)先級(jí)
第二個(gè)參數(shù): 系統(tǒng)保留的參數(shù), 永遠(yuǎn)傳0
*/
// 1.獲取全局的并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0 , 0);
// 2.添加任務(wù)到隊(duì)列
// 文檔說明是FIFO原則, 先進(jìn)先出
// 打印結(jié)果不正確的原因: 線程的執(zhí)行速度可能不一樣, 有得快一些, 有的慢一些
dispatch_async(queue, ^{
NSLog(@"1 - %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2 - %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3 - %@", [NSThread currentThread]);
});
NSLog(@"%s", __func__);
}
- GCD的更多函數(shù)使用(延遲執(zhí)行delay,只執(zhí)行一次once绍傲,快速迭代apply扔傅,柵欄barrier,隊(duì)列組group)請(qǐng)移步GCD更多函數(shù)使用
NSOperation
-
簡(jiǎn)介
- NSOperation是個(gè)抽象類烫饼,并不具備封裝操作的能力猎塞,必須使用它的子類
- 使用NSOperation子類的方式有3種
- NSInvocationOperation
- NSBlockOperation
- 自定義子類繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線程編程
1.先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
2.然后將NSOperation對(duì)象添加到NSOperationQueue中
3.系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來
4.將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行NSOperation使用注意
用NSOperation的子類創(chuàng)建的任務(wù)如果沒有加入隊(duì)列中杠纵,需要手動(dòng)調(diào)用start方法邢享,且默認(rèn)不會(huì)增加新線程是在主線程中執(zhí)行,加入隊(duì)列中系統(tǒng)會(huì)自動(dòng)執(zhí)行
用NSBlockOperation創(chuàng)建任務(wù)后調(diào)用
addExecutionBlock:
追加的任務(wù)默認(rèn)是在子線程中執(zhí)行如果用NSOperationQueue創(chuàng)建了隊(duì)列淡诗,且任務(wù)加入了隊(duì)列中,就不需要調(diào)用start方法伊履,系統(tǒng)會(huì)自動(dòng)從queue中取出任務(wù)在子線程中執(zhí)行
-
NSOperationQueue創(chuàng)建隊(duì)列后的快速添加任務(wù)方法
// 此方法系統(tǒng)內(nèi)部會(huì)自動(dòng)封裝成一個(gè)NSBlockOperation然后再添加到隊(duì)列中 [queue addOperationWithBlock:^{ NSLog(@"4---%@",[NSThread currentThread]); }];
NSOperation的
自定義任務(wù)
韩容,繼承自NSOperation,實(shí)現(xiàn)main方法唐瀑,在main方法中輸入需要執(zhí)行的任務(wù)群凶,將自定義的任務(wù)加入隊(duì)列后,系統(tǒng)會(huì)自動(dòng)在子線程中執(zhí)行任務(wù)哄辣。
#import "SFWOperation.h" // SFWOperation是自定義的繼承自NSOperation的類
@implementation SFWOperation
// 只要將任務(wù)添加到了隊(duì)列中请梢,系統(tǒng)會(huì)自動(dòng)執(zhí)行自定義任務(wù)中的main方法
- (void)main{
// 在這里寫上需要執(zhí)行的任務(wù)
NSLog(@"%@",[NSThread currentThread]);
for (int i = 0; i< 10000; i++) {
NSLog(@"%d",i);
}
}
@end
參數(shù)的使用
-
maxConcurrentOperationCount最大并發(fā)數(shù)
- 自己創(chuàng)建的隊(duì)列
默認(rèn)是并發(fā)
,如果設(shè)置maxConcurrentOperationCount = 1,就是串行 - 注意: 不能設(shè)置為0, 如果設(shè)置為0就不執(zhí)行任務(wù)
- 默認(rèn)情況下maxConcurrentOperationCount = -1
- 在開發(fā)中并發(fā)數(shù)最多盡量不要超過5~6條
- 自己創(chuàng)建的隊(duì)列
-
suspended暫停任務(wù)
- 只要設(shè)置隊(duì)列的suspended為YES,那么就會(huì)暫停隊(duì)列中其它任務(wù)的執(zhí)行
- 也就是說不會(huì)再繼續(xù)執(zhí)行沒有執(zhí)行到的任務(wù)
- 只要設(shè)置隊(duì)列的suspended為NO, 那么就會(huì)恢復(fù)隊(duì)列中其它任務(wù)的執(zhí)行
- 注意:
- 設(shè)置為暫停之后, 不會(huì)立即暫停
- 會(huì)繼續(xù)執(zhí)行當(dāng)前正在執(zhí)行的任務(wù), 直到當(dāng)前任務(wù)執(zhí)行完畢, 就不會(huì)執(zhí)行下一個(gè)任務(wù)了
- 也就是說, 暫停其實(shí)是暫停下一個(gè)任務(wù), 而不能暫停當(dāng)前任務(wù)
- 暫停是可以恢復(fù)的
-
cancelAllOperations取消任務(wù)方法
- 取消隊(duì)列中所有的任務(wù)的執(zhí)行
- 取消和暫停一樣, 是取消后面的任務(wù), 不能取消當(dāng)前正在執(zhí)行的任務(wù)
- 注意: 取消是不可以恢復(fù)的
-
addDependency:依賴方法
// op3 依賴 op1,只有等op1的操作做完了以后才會(huì)開始o(jì)p3的操作 [op3 addDependency:op1];
-
completionBlock:監(jiān)聽方法
- (void (^)(void))completionBlock; - (void)setCompletionBlock:(void (^)(void))block;
線程間通信
- 1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
- 在1個(gè)線程中執(zhí)行完特定任務(wù)后力穗,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
- 常用方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// waitUntilDone的含義:
如果傳入的是YES: 那么會(huì)等到主線程中的方法執(zhí)行完畢, 才會(huì)繼續(xù)執(zhí)行下面其他行的代碼
如果傳入的是NO: 那么不用等到主線程中的方法執(zhí)行完畢, 就可以繼續(xù)執(zhí)行下面其他行的代碼
- 從子線程回到主線程(加入主隊(duì)列)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行耗時(shí)的異步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程毅弧,執(zhí)行UI刷新操作
});
});
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 創(chuàng)建隊(duì)列,獲取全局并行隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 添加下載任務(wù)到子線程中
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
// 下載圖片
NSURL *url = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/1b4c510fd9f9d72aee889e1fd22a2834359bbbc0.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 顯示UI控件当窗,將此任務(wù)加入到主隊(duì)列中
// 如果是通過異步函數(shù)添加任務(wù)够坐,會(huì)先執(zhí)行完所有代碼再來執(zhí)行block中的任務(wù)
// 如果是通過同步函數(shù)添加的任務(wù),會(huì)先執(zhí)行完block中的任務(wù)再執(zhí)行其他代碼
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
self.imageView.image = image;
});
});
}