多線程,java多線程
一、iOS中多線程的實現方案
二、NSThread1. 建立和啟動線程
一個NSThread對象就代表一條線程
建立、啟動線程
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // 建立並開啟一條子線程 4 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"參數"]; 5 // 線程一啟動,就會線上程thread中執行self的run方法 6 [thread start]; 7 } 8 /** 9 * 開啟子線程10 *11 * @param param object傳進來的參數12 */13 - (void)run:(NSString *)param14 {15 NSLog(@"currentThread:%@--run--%@", [NSThread currentThread], param);16 }17 18 列印結果:19 <NSThread: 0x7fd5b2f207f0>{number = 2, name = (null)}--run--參數
主線程相關用法
1 + (NSThread *)mainThread; // 獲得主線程2 - (BOOL)isMainThread; // 是否為主線程3 + (BOOL)isMainThread; // 是否為主線程
其他用法
獲得當前線程NSThread *current = [NSThread currentThread];線程的調度優先順序+ (double)threadPriority;+ (BOOL)setThreadPriority:(double)p;- (double)threadPriority;- (BOOL)setThreadPriority:(double)p;調度優先順序的取值範圍是0.0 ~ 1.0,預設0.5,值越大,優先順序越高線程的名字- (void)setName:(NSString *)n;- (NSString *)name;
其他建立線程方式
建立線程後自動啟動線程[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];隱式建立並啟動線程[self performSelectorInBackground:@selector(run) withObject:nil];
上述2種建立線程方式的優缺點
2. 控制線程狀態
啟動線程- (void)start; // 進入就緒狀態 -> 運行狀態。當線程任務執行完畢,自動進入死亡狀態阻塞(暫停)線程+ (void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 進入阻塞狀態強制停止線程+ (void)exit;// 進入死亡狀態
注意:一旦線程停止(死亡)了,就不能再次開啟任務
3. 互斥鎖
互斥鎖使用格式:@synchronized(鎖對象) { // 需要鎖定的代碼 }
注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
互斥鎖的優缺點
- 優點:能有效防止因多線程搶奪資源造成的資料安全問題
- 缺點:需要消耗大量的CPU資源
互斥鎖的使用前提:多條線程搶奪同一塊資源
相關專業術語:線程同步
線程同步的意思是:多條線程按順序地執行任務
互斥鎖,就是使用了線程同步技術
4. 原子和非原子屬性
OC在定義屬性時有nonatomic和atomic兩種選擇
- atomic:原子屬性,為setter方法加鎖(預設就是atomic)
- nonatomic:非原子屬性,不會為setter方法加鎖
atomic加鎖原理
1 @property (assign, atomic) int age;2 3 - (void)setAge:(int)age4 {5 @synchronized(self) {6 _age = age;7 }8 }
nonatomic和atomic對比
- atomic:安全執行緒,需要消耗大量的資源
- nonatomic:非安全執行緒,適合記憶體小的行動裝置
5. 線程間通訊
線程間通訊的體現
1個線程傳遞資料給另1個線程
在1個線程中執行完特定任務後,轉到另1個線程繼續執行任務
線程間通訊常用方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
三、GCD1. 簡介
全稱是Grand Central Dispatch,純C語言,提供了非常多強大的函數。
GCD的優勢
- GCD是蘋果公司為多核的並行運算提出的解決方案
- GCD會自動利用更多的CPU核心(比如雙核、四核)
- GCD會自動管理線程的生命週期(建立線程、調度任務、銷毀線程)
- 程式員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼
2. 任務和隊列
GCD中有2個核心概念
將任務添加到隊列中:
- GCD會自動將隊列中的任務取出,放到對應的線程中執行
- 任務的取出遵循隊列的FIFO原則:先進先出,後進後出
3. 執行任務
GCD中有2個用來執行任務的函數
- 用同步的方式執行任務,queue:隊列 block:任務
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
同步和非同步區別
- 同步:在當前線程中執行
- 非同步:在另一條線程中執行
4. 隊列的類型
GCD的隊列可以分為2大類型
- 並發隊列(Concurrent Dispatch Queue):可以讓多個任務並發(同時)執行(自動開啟多個線程同時執行任務),並發功能只有在非同步(dispatch_async)函數下才有效
- 串列隊列(Serial Dispatch Queue):讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)
5. 並發隊列
GCD預設已經提供了全域的並發隊列,供整個應用使用,不需要手動建立
使用dispatch_get_global_queue函數獲得全域的並發隊列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority, // 隊列的優先順序unsigned long flags); // 此參數暫時無用,用0即可dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全域並發隊列
全域並發隊列的優先順序
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 預設(中)#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 後台
6. 串列隊列
GCD中獲得串列有2種途徑
使用dispatch_queue_create函數建立串列隊列
dispatch_queue_tdispatch_queue_create(const char *label, // 隊列名稱 dispatch_queue_attr_t attr); // 隊列屬性,一般用NULL即可dispatch_queue_t queue = dispatch_queue_create("queue", NULL); // 建立dispatch_release(queue); // 非ARC需要釋放手動建立的隊列
使用主隊列(跟主線程相關聯的隊列)
主隊列是GCD內建的一種特殊的串列隊列
放在主隊列中的任務,都會放到主線程中執行
使用dispatch_get_main_queue()獲得主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
7. 各種隊列的執行效果
8. 線程間通訊樣本
從子線程回到主線程
1 dispatch_async(2 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{3 // 執行耗時的非同步作業...4 dispatch_async(dispatch_get_main_queue(), ^{5 // 回到主線程,執行UI重新整理操作6 });7 });
9. 延時執行
iOS常見的延時執行有2種方式
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];// 2秒後再調用self的run方法
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒後非同步執行這裡的代碼...});
10. 一次性代碼
使用dispatch_once函數能保證某段代碼在程式運行過程中只被執行1次
1 static dispatch_once_t onceToken;2 dispatch_once(&onceToken, ^{3 // 只執行1次的代碼(這裡面預設是安全執行緒的)4 });
11. 隊列組
有這麼1種需求
首先:分別非同步執行2個耗時的操作
其次:等2個非同步作業都執行完畢後,再回到主線程執行操作
如果想要快速高效地實現上述需求,可以考慮用隊列組
1 dispatch_group_t group = dispatch_group_create(); 2 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 3 // 執行1個耗時的非同步作業 4 }); 5 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 6 // 執行1個耗時的非同步作業 7 }); 8 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 9 // 等前面的非同步作業都執行完畢後,回到主線程...10 });
12. 單例模式
單例模式可以保證在程式運行過程,一個類只有一個執行個體,而且該執行個體易於供外界訪問。從而方便地控制了執行個體個數,並節約系統資源。
單例模式在ARC\MRC環境下的寫法有所不同,需要編寫2套不同的代碼
可以用宏判斷是否為ARC環境
#if __has_feature(objc_arc)// ARC#else// MRC#endif
ARC中,單例模式的實現:
1. 在.m中保留一個全域的static的執行個體
static id _instance;
2. 重寫allocWithZone:方法,在這裡建立唯一的執行個體(注意安全執行緒)
1 + (id)allocWithZone:(struct _NSZone *)zone2 {3 @synchronized(self) {4 if (!_instance) {5 _instance = [super allocWithZone:zone];6 }7 }8 return _instance;9 }
3. 提供1個類方法讓外界訪問唯一的執行個體
1 + (instancetype)sharedSoundTool2 {3 @synchronized(self) {4 if (!_instance) {5 _instance = [[self alloc] init];6 }7 }8 return _instance;9 }
非ARC中(MRC),單例模式的實現(比ARC多了幾個步驟)
1. 實現copyWithZone:方法
1 + (id)copyWithZone:(struct _NSZone *)zone2 {3 return _instance;4 }
2. 實現記憶體管理方法
1 - (id)retain { return self; }2 - (NSUInteger)retainCount { return 1; }3 - (oneway void)release {}4 - (id)autorelease { return self; }
四、NSOperation
NSOperation的作用:
配合使用NSOperation和NSOperationQueue也能實現多線程編程
NSOperation和NSOperationQueue實現多線程的具體步驟:
- 先將需要執行的操作封裝到一個NSOperation對象中
- 然後將NSOperation對象添加到NSOperationQueue中
- 系統會自動將NSOperation中封裝的操作放到一條新線程中執行
1. NSOperation的子類
NSOperation是個抽象類別,並不具備封裝操作的能力,必須使用它的子類
使用NSOperation子類的方式有3種
- NSInvocationOperation
- NSBlockOperation
- 自訂子類繼承NSOperation,實現內部相應的方法
NSInvocationOperation
建立NSInvocationOperation對象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
調用start方法開始執行操作
- (void)start;
一旦執行操作,就會調用target的sel方法
注意:
- 預設情況下,調用了start方法後並不會開一條新線程去執行操作,而是在當前線程同步執行操作
- 只有將NSOperation放到一個NSOperationQueue中,才會非同步執行操作
NSBlockOperation
建立NSBlockOperation對象
+ (id)blockOperationWithBlock:(void (^)(void))block;
通過addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封裝的運算元 > 1,就會非同步執行操作
2. NSOperationQueue
NSOperationQueue的作用:NSOperation可以調用start方法來執行任務,但預設是同步執行的。如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統會自動非同步執行NSOperation中的操作。
添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;- (void)addOperationWithBlock:(void (^)(void))block;
2.1 最大並發數
並發數也就是同時執行的任務數。比如,同時開3個線程執行3個任務,並發數就是3。
最大並發數的相關方法
- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
3. 隊列的取消、暫停、恢複
取消隊列的所有操作
- (void)cancelAllOperations;
也可以調用NSOperation的- (void)cancel方法取消單個操作
暫停和恢複隊列
- (void)setSuspended:(BOOL)b; // YES代表暫停隊列,NO代表恢複隊列- (BOOL)isSuspended;
4. 操作優先順序
設定NSOperation在queue中的優先順序,可以改變操作的執行順序
- (NSOperationQueuePriority)queuePriority;- (void)setQueuePriority:(NSOperationQueuePriority)p;
優先順序的取值(優先順序越高,越先執行)
NSOperationQueuePriorityVeryLow = -8L,NSOperationQueuePriorityLow = -4L,NSOperationQueuePriorityNormal = 0,NSOperationQueuePriorityHigh = 4,NSOperationQueuePriorityVeryHigh = 8
5. 操作依賴
NSOperation之間可以設定依賴來保證執行順序,比如一定要讓操作A執行完後,才能執行操作B,可以這麼寫:
[operationB addDependency:operationA]; // 操作B依賴於操作A
可以在不同queue的NSOperation之間建立依賴關係
注意:不能相互依賴,比如A依賴B,B依賴A
6. 操作的執行順序
對於添加到queue中的operations,它們的執行順序取決於2點:
- 首先依據NSOperation之間的依賴關係
- 然後依據NSOperation的優先順序
因此,總體的執行順序是:先滿足依賴關係,然後再從NSOperation中選擇優先順序最高的那個執行
7. 自訂NSOperation
自訂NSOperation的步驟很簡單
重寫- (void)main方法,在裡面實現想執行的任務
重寫- (void)main方法的注意點
- 自己建立自動釋放池(因為如果是非同步作業,無法訪問主線程的自動釋放池)
- 經常通過- (BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應