(atomic是隻對set加鎖,對get不加鎖,因此在獲取的時候會有偏差)多線程
synchronized的使用形式是async
@synchronized (object) { //須要保護的代碼塊 }
設計的一個類測試
@interface SynchronizObj : NSObject - (void)synchronizMethod; - (void)normalMethod; @end @implementation SynchronizObj - (void)synchronizMethod { @synchronized (self) { NSLog(@"synchronizMethod"); sleep(5); } } - (void)testLock { static NSObject *lock = nil; if (!lock) { lock = [[NSString alloc] init]; } @synchronized(lock){ NSLog(@"開始枷鎖"); sleep(5); } } - (void)normalMethod { NSLog(@"normalMethod"); } @end
(1)直接調用這個類atom
SynchronizObj *obj = [[SynchronizObj alloc] init]; [obj synchronizMethod]; [obj normalMethod];
獲得的結果是spa
19:31:09.855 LockDemo[1120:135454] synchronizMethod 19:31:14.857 LockDemo[1120:135454] normalMethod
相差了5秒執行.操作系統
(2)咱們把這個放到異步執行裏面看一下如何.net
dispatch_async(dispatch_get_main_queue(), ^{ [obj testLock]; }); dispatch_async(dispatch_get_main_queue(), ^{ sleep(1);//讓synchronizMethod先執行 [obj testLock]; }); 結果 22:12:21.745 LockDemo[1144:141456] 開始枷鎖 22:12:27.889 LockDemo[1144:141446] 開始枷鎖
放到異步裏,只相差6秒執行,能夠看到,testLock中@synchronized保護的代碼塊,在未返回的狀況下,其餘線程是沒法訪問。線程
(3)@synchronized須要一個參數,若是傳遞的是不一樣的參數呢?咱們把測試的類的代碼修改一下設計
- (void)synchronizMethod:(id)obj; - (void)normalMethod:(id)obj; - (void)synchronizMethod:(id)obj { @synchronized (self) { NSLog(@"synchronizMethod:%@", obj); sleep(5); } } - (void)normalMethod:(id)obj { @synchronized (self) { NSLog(@"normalMethod"); } }
調用代碼和結果
SynchronizObj *obj = [[SynchronizObj alloc] init]; SynchronizObj *obj1 = [[SynchronizObj alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [obj synchronizMethod:obj]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);//讓obj synchronizMethod先執行 [obj normalMethod:obj]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);//讓obj synchronizMethod先執行 [obj1 synchronizMethod:obj]; }); 結果 20:05:46.491 LockDemo[1199:156733] synchronizMethod:<SynchronizObj: 0x7fd632f0b000> 20:05:47.492 LockDemo[1199:156740] synchronizMethod:<SynchronizObj: 0x7fd632f0b000> 20:05:51.497 LockDemo[1199:156744] normalMethod
能夠看到,多線程中,@synchronized獲取的是不一樣的參數,是不會阻塞的,但若是是同一個參數,則只有一個會得到鎖,直到鎖保護的內容執行完畢。
·NSLock
NSLock等是實現了NSLocking協議,因此具有了lock和unlock這一對上鎖解鎖的方法,這一對方法須要在同一線程中,不然會致使報錯(先調用了unlock,沒有或者後調用了lock)或者沒法持有鎖(上一個lock未調用unlock)。
通常是這樣調用
NSLock* lock = [[NSLock alloc] init]; dispatch_async(dispatch_get_main_queue(), ^{ [lock lock]; NSLog(@"任務1"); sleep(1); [lock unlock]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);// 讓任務1先執行 [lock lock]; NSLog(@"任務2"); [lock unlock]; }); 結果 20:23:43.869 LockDemo[1263:169163] 任務1 20:23:44.870 LockDemo[1263:169200] 任務2
·NSConditionLock
NSConditionLock顧名思義,叫作條件鎖,根據某種條件進行上鎖解鎖,但這個條件,必須是個NSInteger變量。
NSConditionLock有這麼幾個特別的地方:
- (instancetype)initWithCondition:(NSInteger)condition; @property (readonly) NSInteger condition; - (void)lockWhenCondition:(NSInteger)condition; - (void)unlockWithCondition:(NSInteger)condition;
NSConditionLock有個condition屬性,只讀的,使用上面兩個方法進行上鎖和解鎖的時候,lockWhenCondition時,會將參數condition和自身的condition屬性進行比較,若是相同,才能進行上鎖操做,不然會處於等待;unlockWithCondition時,會將參數condition自動賦值給自身的condition屬性,用於以後的對比。一樣,調用initWithCondition初始化,也會將condition賦值。
文檔中用生產和消費舉例,下面是個人測試代碼和結果
NSConditionLock* lock = [[NSConditionLock alloc] init]; NSMutableArray* products = [NSMutableArray array]; #define hasData YES #define noData NO dispatch_async(dispatch_get_main_queue(), ^{ for (int i = 0; i < 3; i ++) { [lock lockWhenCondition:noData]; [products addObject:@"product"]; NSLog(@"生產產品%d condition:%ld", i, lock.condition); [lock unlockWithCondition:hasData]; } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);// 讓任務1先執行 for (int i = 0; i < 3; i ++) { [lock lockWhenCondition:hasData]; [products removeAllObjects]; NSLog(@"消費產品%d condition:%ld", i, lock.condition); [lock unlockWithCondition:noData]; } }); 結果 20:45:59.579 LockDemo[1297:184273] 生產產品0 condition:0 20:46:00.580 LockDemo[1297:184322] 消費產品0 condition:1 20:46:00.580 LockDemo[1297:184273] 生產產品1 condition:0 20:46:00.581 LockDemo[1297:184322] 消費產品1 condition:1 20:46:00.581 LockDemo[1297:184273] 生產產品2 condition:0 20:46:00.581 LockDemo[1297:184322] 消費產品2 condition:1
·NSRecursiveLock
NSRecursiveLock,遞歸鎖,容許在同一線程中屢次持有鎖,但每次持有必須配對解鎖。
@property (nonatomic, strong) NSRecursiveLock* lock; - (void)testMethod { _lock = [[NSRecursiveLock alloc] init]; [self testRecursiveLock:5]; } - (void)testRecursiveLock:(NSInteger)value { [_lock lock]; if (value > 0) { NSLog(@"第%ld次持有鎖", 6 - value); -- value; [self testRecursiveLock:value]; } [_lock unlock]; } 結果 21:06:27.322 LockDemo[1327:190602] 第1次持有鎖 21:06:27.323 LockDemo[1327:190602] 第2次持有鎖 21:06:27.323 LockDemo[1327:190602] 第3次持有鎖 21:06:27.323 LockDemo[1327:190602] 第4次持有鎖 21:06:27.323 LockDemo[1327:190602] 第5次持有鎖
不一樣線程持有鎖,按照基本狀況,即先持有鎖的線程先執行,直到最終釋放鎖。好比對上面的代碼作修改,在遞歸期間,有另外一線程請求鎖
- (void)testMethod { _lock = [[NSRecursiveLock alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);// 讓遞歸程序先執行 [_lock lock]; NSLog(@"異步加鎖"); sleep(5); NSLog(@"異步解鎖"); [_lock unlock]; }); [self testRecursiveLock:5]; } - (void)testRecursiveLock:(NSInteger)value { [_lock lock]; if (value > 0) { NSLog(@"第%ld次持有鎖", 6 - value); sleep(1); -- value; [self testRecursiveLock:value]; } [_lock unlock]; } 結果 21:12:03.876 LockDemo[1357:194699] 第1次持有鎖 21:12:04.877 LockDemo[1357:194699] 第2次持有鎖 21:12:05.878 LockDemo[1357:194699] 第3次持有鎖 21:12:06.879 LockDemo[1357:194699] 第4次持有鎖 21:12:07.879 LockDemo[1357:194699] 第5次持有鎖 21:12:08.881 LockDemo[1357:194735] 異步加鎖 21:12:13.884 LockDemo[1357:194735] 異步解鎖
上面的測試代碼,先在主線程持有遞歸鎖,其餘線程以後請求鎖的時候,須要等待主線程的遞歸鎖把鎖保護內容執行完畢,即遞歸調用完成,而不是在某一次的unlock後就得到鎖資源。
·NSCondition
NSCondition看起來和NSConditionLock很像,但其實差異仍是挺大的。
文檔中推薦NSCondition的使用步驟是
NSCondition的使用和POSIX很相似,都須要加(推薦加)boolean_predicate來確保這個過程。由於操做系統的設計,容許NSCondition返回一個虛假的成功信號,在沒有觸發你的代碼狀況下,喚醒NSCondition鎖住的線程。
測試代碼以下
@property (atomic, assign) BOOL ready_to_go; NSCondition* condition = [[NSCondition alloc] init]; _ready_to_go = NO; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [condition lock]; if (_ready_to_go == NO) { NSLog(@"等待1"); [condition wait]; } NSLog(@"執行1"); [condition unlock]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(3); [condition lock]; _ready_to_go = YES; NSLog(@"執行2"); [condition broadcast];//觸發全部等待condition的線程 // 也可使用這個[condition signal]; [condition unlock]; }); 結果 21:48:04.434 LockDemo[1392:217171] 等待1 21:48:07.435 LockDemo[1392:217182] 執行2 21:48:07.435 LockDemo[1392:217171] 執行1
雖然任務1先執行,但因爲標識不符合,因此進入等待,任務2執行後,更新標識,才觸發到任務1的執行。