1.什么是线程安全?
线程安全,是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
2.atomic与nonatomic的区别 atomic 与 nonatomic 的本质区别在于getter
、setter
方法实现上的不同:
#示例1:nonatomic属性的实现
1 2 3 4 5 6 7 8 9 10 11 12 - (NSMutableArray *)mutArr { return _mutArr; } - (void)setMutArr: (NSMutableArray *)new Arr { [new Arr retain]; [_mutArr release]; _mutArr = new Arr ; }
#示例2:atomic属性的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (NSMutableArray *)mutArr { @synchronized (self ) { return _mutArr; } } - (void )setMutArr:(NSMutableArray *)newArr { @synchronized (self ) { [_mutArr release]; _mutArr = [newArr retain ]; } }
nonatomic 修饰的对象不保证setter
和getter
的完整性,多线程访问它时可能会返回未初始化的对象。nonatomic 比 atomic 快,但它不是线程安全的。
atomic 修饰的对象会保证setter
和getter
的完整性,任何线程对其访问都可以得到一个完整的初始化后的对象。例如有多个线程同时调用setter
,不会出现某个线程执行完setter
全部语句之前,另一个线程开始执行setter
情况,相当于函数头尾加了锁一样。
3.atomic与真正的线程安全 atomic 只保证了属性的存取方法是完整的,但并不保证整个属性”对象”是线程安全的,因为别的线程还能进行其他操作。比如,线程A
执行 [self.mutArr addObject:obj];
,同一时间线程B
执行self.mutArr[index]
就不是线程安全的。多个线程同时调用setter
和getter
时获得的对象值可能会不一样,甚至发生崩溃。想要绝对的线程安全就要用到线程的同步机制,比如使用NSLock
、@synchronized
等加锁。
#示例3:数据同步(加锁)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 @interface AppDelegate ()@property (atomic, strong ) NSMutableArray *mArr;@end @implementation AppDelegate - (BOOL )application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self .mArr = [NSMutableArray arrayWithObjects:@"1" ,@"2" ,@"3" ,@"4" , nil ]; NSOperationQueue * queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 2 ; __weak typeof (self ) weakSelf = self ; NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{ __strong typeof (weakSelf) strongSelf = weakSelf; @synchronized (self ){ for (NSString * str in strongSelf.mArr){ NSLog (@"1读数据:%@" ,str); } } }]; NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{ __strong typeof (weakSelf) strongSelf = weakSelf; @synchronized (self ){ for (NSInteger i = 0 ; i < 10 ; i++) { NSString *str = [NSString stringWithFormat:@"%@%ld" ,@"字符串" ,i]; NSLog (@"++++2写数据:%@" ,str); [strongSelf.mArr addObject:str]; } } }]; NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{ __strong typeof (weakSelf) strongSelf = weakSelf; @synchronized (self ){ for (NSString * str in strongSelf.mArr){ NSLog (@"3读数据:%@" ,str); } } }]; [queue addOperation:operation1]; [queue addOperation:operation2]; [queue addOperation:operation3]; return YES ; }
示例中,数组被定义为 atomic 的属性,但是在不加锁的情况下,会出现崩溃,报错“Collection <__NSArrayM: 0x60000024a8f0> was mutated while being enumerated.”,说明 atomic 对象并不是线程安全的。示例中提供了两种数据同步的方式,一种是使用NSLock
进行加锁,另一种是使用@synchronized
加锁,两种都可以保证数组对象的线程安全。
综上,在无需考虑多线程的情况下使用 nonatomic,它能让编译器少生成一些加锁的代码,提高读写效率。在多线程环境下使用 atomic,同时配合加锁,防止产生该变量读写等操作的同步问题。