面试题答案
一键面试线程安全问题及举例
在多线程环境下,Objective-C 的懒加载模式可能出现以下线程安全问题:
- 多次初始化:多个线程同时判断属性为
nil
,进而都尝试进行初始化,导致属性被多次初始化。例如,假设有一个单例类使用懒加载模式创建单例实例:
@interface Singleton : NSObject
@property (nonatomic, strong) static Singleton *sharedInstance;
+ (instancetype)sharedInstance;
@end
@implementation Singleton
+ (instancetype)sharedInstance {
if (!_sharedInstance) {
_sharedInstance = [[self alloc] init];
}
return _sharedInstance;
}
@end
在多线程环境下,可能会有多个线程同时进入 if (!_sharedInstance)
判断,都认为 _sharedInstance
为 nil
,从而创建多个实例。
解决方法及其优缺点
- 使用
@synchronized
关键字- 实现方式:
+ (instancetype)sharedInstance {
@synchronized(self) {
if (!_sharedInstance) {
_sharedInstance = [[self alloc] init];
}
}
return _sharedInstance;
}
- **优点**:实现简单,只需在关键代码块添加 `@synchronized` 即可。
- **缺点**:性能较低,因为 `@synchronized` 使用的是互斥锁,会阻塞其他线程,影响多线程并发性能。特别是在频繁调用懒加载的情况下,性能瓶颈会比较明显。
2. 使用 dispatch_once
- 实现方式:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- **优点**:性能高,`dispatch_once` 只执行一次初始化代码,并且采用了高效的实现机制,不会对其他线程造成阻塞。它在底层使用了原子操作和内存屏障,保证了线程安全和初始化的唯一性。
- **缺点**:只能用于初始化一次的场景,如果需要动态重新初始化属性,`dispatch_once` 就不适用了。而且对于非单例的懒加载属性,使用 `dispatch_once` 需要一些额外的设计来管理 `dispatch_once_t` 变量的作用域。