1、原子属性:
1-1、nonatomic与atomic:
nonatomic
: 非原子属性;atomic
: 原子属性;- 线程安全的,针对多线程设计的属性修饰符,是默认值.
- 保证同一时间只有一个线程能够写入,但是同一个时间多个线程都可以读取;
- 单写多读 : 单个线程写入
write
,多个线程可以读取read
; atomic
本身就有一把锁,自旋锁
。
1-2、nonatomic
和atomic
对比:
nonatomic
: 非线程安全,适合内存小的移动设备;atomic
: 线程安全,需要消耗大量的资源.性能比非原子属性要差。
1-3、iOS开发的建议:
- 所有属性都声明为
nonatomic
,性能更高; - 尽量避免多线程抢夺同一块资源;
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
2、模拟原子属性:
2-1、定义属性:
1./// 非原子属性[email protected] (nonatomic,strong) NSObject *obj1;3./// 原子属性:内部有"自旋锁"[email protected] (atomic,strong) NSObject *obj2;5./// 模拟原子属性[email protected] (atomic,strong) NSObject *obj3;
?了解:
重写非原子属性的setter
和getter
方法:
?了解:
1、重写了原子属性的setter
方法之后,会覆盖原子属性内部的自旋锁
,使其失效;然后我们加入互斥锁
,来模拟单写多读
。
?了解:
2、重写了属性的setter
和getter
方法之后,系统就不会再帮我们生成待下划线的成员变量;使用合成指令@synthesize
,就可以手动的生成带下划线的成员变量。
示例程序:
1.// 合成指令[email protected] obj3 = _obj3;3.4./// obj3的setter方法5.- (void)setObj3:(NSObject *)obj36.{7. @synchronized(self) {8. _obj3 = obj3;9. }10.}11.12./// obj3的getter方法13.- (NSObject *)obj314.{15. return _obj3;16.}
2-2、性能测试:
1./// 测试"非原子属性","互斥锁","自旋锁"的性能2.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event3.{4. NSInteger largeNum = 1000*1000;5.6. NSLog(@"非原子属性");7. CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();8. for (int i = 0; i < largeNum; i++) {9. self.obj1 = [[NSObject alloc] init];10. }11. NSLog(@"非原子属性 => %f",CFAbsoluteTimeGetCurrent()-start);12.13. NSLog(@"原子属性");14. start = CFAbsoluteTimeGetCurrent();15. for (int i = 0; i < largeNum; i++) {16. self.obj2 = [[NSObject alloc] init];17. }18. NSLog(@"原子属性 => %f",CFAbsoluteTimeGetCurrent()-start);19.20. NSLog(@"模拟原子属性");21. start = CFAbsoluteTimeGetCurrent();22. for (int i = 0; i < largeNum; i++) {23. self.obj3 = [[NSObject alloc] init];24. }25. NSLog(@"模拟原子属性 => %f",CFAbsoluteTimeGetCurrent()-start);26.}
3、互斥锁和自旋锁对比:
3-1、共同点:
- 都能够保证同一时间,只有一条线程执行锁定范围的代码
3-2、不同点:
互斥锁
:如果发现有其他线程正在执行锁定的代码,线程会进入休眠
状态,等待其他线程执行完毕,打开锁之后,线程会重新进入就绪
状态;等待被CPU重新调度。自旋锁
:如果发现有其他线程正在执行锁定的代码,线程会以死循环
的方式;一直等待锁定代码执行完成。
4、开发建议:
- 所有属性都声明为
nonatomic
,原子属性和非原子属性的性能几乎一样。 - 尽量避免多线程抢夺同一块资源。
- 要实现线程安全,必须要用到
锁
;无论什么锁,都是有性能消耗的。 - 自旋锁更适合执行非常短的代码,死循环内部不适合写复杂的代码。
- 尽量将加锁,资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
- 为了流畅的用户体验,UIKit类库的线程都是不安全的;所以我们需要在主线程(UI线程)上更新UI。
- 所有包含
NSMutable
的类都是线程不安全的,在做多线程开发的时候,需要注意多线程同时操作可变对象的线程安全问题。
时间: 2024-10-17 15:17:33