面试题答案
一键面试多线程环境下享元模式可能出现的问题
- 数据竞争:多个线程同时访问和修改享元对象的内部状态时,可能导致数据不一致。例如,享元对象中保存了一些计数器或者状态变量,多个线程同时对其进行增减操作,会得到错误的结果。
- 资源共享冲突:如果享元对象依赖外部资源(如文件、数据库连接等),多线程同时访问这些资源可能会导致资源争用问题,如文件读写混乱、数据库连接异常等。
优化享元模式以适应多线程场景的方法
- 使用锁机制:在访问享元对象的共享状态时,使用互斥锁(
NSLock
、@synchronized
等)来保证同一时间只有一个线程可以访问和修改共享状态。 - 不可变对象:将享元对象设计为不可变对象,这样就无需担心多线程对其状态的修改问题。一旦创建,对象的状态就不再改变。
代码示例 - 使用 @synchronized
实现线程安全
#import <Foundation/Foundation.h>
// 享元工厂类
@interface FlyweightFactory : NSObject
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *flyweights;
+ (instancetype)sharedFactory;
- (id)getFlyweightWithKey:(NSString *)key;
@end
@implementation FlyweightFactory
+ (instancetype)sharedFactory {
static FlyweightFactory *factory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
factory = [[FlyweightFactory alloc] init];
factory.flyweights = [NSMutableDictionary dictionary];
});
return factory;
}
- (id)getFlyweightWithKey:(NSString *)key {
@synchronized(self) {
id flyweight = self.flyweights[key];
if (!flyweight) {
flyweight = [[NSObject alloc] init]; // 实际应用中这里创建具体的享元对象
self.flyweights[key] = flyweight;
}
return flyweight;
}
}
@end
// 测试代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
FlyweightFactory *factory = [FlyweightFactory sharedFactory];
// 模拟多线程环境
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < 10; ++i) {
dispatch_group_async(group, queue, ^{
id flyweight = [factory getFlyweightWithKey:@"key"];
NSLog(@"Thread %ld got flyweight: %@", (long)[NSThread currentThread].threadIdentifier, flyweight);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}
代码示例 - 使用 NSLock
实现线程安全
#import <Foundation/Foundation.h>
// 享元工厂类
@interface FlyweightFactory : NSObject
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *flyweights;
@property (nonatomic, strong) NSLock *lock;
+ (instancetype)sharedFactory;
- (id)getFlyweightWithKey:(NSString *)key;
@end
@implementation FlyweightFactory
+ (instancetype)sharedFactory {
static FlyweightFactory *factory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
factory = [[FlyweightFactory alloc] init];
factory.flyweights = [NSMutableDictionary dictionary];
factory.lock = [[NSLock alloc] init];
});
return factory;
}
- (id)getFlyweightWithKey:(NSString *)key {
[self.lock lock];
id flyweight = self.flyweights[key];
if (!flyweight) {
flyweight = [[NSObject alloc] init]; // 实际应用中这里创建具体的享元对象
self.flyweights[key] = flyweight;
}
[self.lock unlock];
return flyweight;
}
@end
// 测试代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
FlyweightFactory *factory = [FlyweightFactory sharedFactory];
// 模拟多线程环境
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < 10; ++i) {
dispatch_group_async(group, queue, ^{
id flyweight = [factory getFlyweightWithKey:@"key"];
NSLog(@"Thread %ld got flyweight: %@", (long)[NSThread currentThread].threadIdentifier, flyweight);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}
以上两种方式都通过加锁保证了享元对象在多线程环境下的安全访问,@synchronized
语法糖内部也是基于锁机制实现,而 NSLock
则是更显式的锁操作。在实际应用中,可根据具体场景选择合适的方式。