可能出现的线程安全问题
- 多次实例化:在多线程环境下,如果没有适当的同步机制,多个线程可能同时判断单例实例为
nil
,进而各自创建一个实例,导致单例不再是唯一实例。
- 数据竞争:当单例对象的属性或内部数据被多个线程同时访问和修改时,可能会出现数据不一致的情况。
确保单例线程安全的方法及其原理
1. 使用 dispatch_once
- 原理:
dispatch_once
是基于 GCD(Grand Central Dispatch)的函数,它会保证代码块只被执行一次,无论有多少线程同时调用。它内部使用了一个静态变量来记录代码块是否已经执行过,并且通过底层的原子操作来确保线程安全。
- 代码示例:
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
@property (nonatomic, strong, readonly) NSString *name;
+ (instancetype)sharedInstance;
@end
@implementation Singleton
static Singleton *sharedInstance = nil;
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.name = @"Singleton Object";
});
return sharedInstance;
}
@end
2. 使用 synchronized
关键字
- 原理:
synchronized
会为传入的对象(通常是类对象或单例实例本身)创建一个互斥锁。当一个线程进入 synchronized
代码块时,会获取锁,其他线程必须等待锁被释放才能进入。这样就保证了同一时间只有一个线程能执行创建单例实例的代码。
- 代码示例:
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
@property (nonatomic, strong, readonly) NSString *name;
+ (instancetype)sharedInstance;
@end
@implementation Singleton
static Singleton *sharedInstance = nil;
+ (instancetype)sharedInstance {
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
sharedInstance.name = @"Singleton Object";
}
}
return sharedInstance;
}
@end