(五十七)线程的资源共享、单例的实现

【资源共享的问题】

例如线程A与B均实现数字Num的加一操作,如果不加以限制,可能A和B先后拿到最初的Num,然后返回Num+1,无法实现Num+1之后再+1。

【互斥锁】

使用互斥锁(@synchronized)来解决,让线程A操作时锁住Num,不允许B的读写,直到A操作完并且写回后,再让B进行工作,再锁住Num,直到B操作完毕,再解开锁,类似于上厕所,在厕所内要锁门一样。

使用@synchronized(self){......}包装在内的为互斥锁的作用范围,会严重降低效率。因此应尽可能的减小范围。不推荐使用。

【原子锁】

一般的成员变量都写为nonatomic,即为非原子锁,如果写为atomic就是原子锁,这样的变量只支持多读单写,原子锁限制了写入,对读取没有影响,原子锁的性能影响较小。

【并发编程的主要目的】

并发编程最主要的目的是提高性能,让更多的代码同时运行,提高整体性能。

由于手机端一般不必解决资源抢夺问题,因此一般不会涉及到资源抢夺。

Tip:线程的休眠方法:

[NSThread sleepForTimeInterval:0.1f];

Tip:注意多线程开发永远不要相信一次运行结果。

【UI都在主线程的原因】

出于性能考虑,UIKit中绝大多数的类都不是线程安全的,因此,官方要求更新UI的相关操作应该在主线程中执行。


【单例设计模式】

如UIApplication、音频播放可以保证音乐一直播放,不随着视图控制器的改变而受到影响。

再如一些硬件资源,例如加速计、屏幕(UIScreen mainScreen),带有sharedXxx和mainXxx关键字的常常都是单例。

单例在面试中可能会要求手写。

单例的实现步骤:

1.任何对象的alloc方法都会最终调用allocWithZone来实例化,因此重写allocWithZone来实现初始化结果的唯一性。

2.在allocWihtZone方法中,利用dispatch_once保证调用父类实现初始化的代码只被执行一次,最后返回对象本身。

为了保证单例不被销毁,使用一个静态指针,静态指针存放在堆中,直到应用程序终止后才会被销毁。

// 在iOS中,所有对象的内存空间分配,最终都会调用allocWithZone方法
// 制作单例,需要重写此方法
+ (id)allocWithZone:(struct _NSZone *)zone{
    
    static DemoObj *instance;
    
    // dispatch_once_t是线程安全的
    static dispatch_once_t onceToken;
    // dispatch_once宏可以保证快代码的指令只被执行一次
    dispatch_once(&onceToken, ^{
        // 只会被执行一次
        instance = [super allocWithZone:zone];
    });
    
    return instance;
    
}

3.写一个sharedXxx方法用于返回单例对象,由于alloc已经唯一,因此直接在shared方法中返回一个初始化的实例即可:

+ (instancetype)sharedDemoObj{
    
    return [[self alloc] init];
    
}

单例的缺点:单例对象一旦被创建,对象指针会存放在静态区,在堆中分配空间,会在应用程序终止后才会释放。

另一种单例的实现:注意这样是不好的。

static DemoObj2 *instance;

@implementation DemoObj2

+ (instancetype)sharedDemoObj2{
    
    if (!instance) {
        instance = [[self alloc] init];
    }
    
    return instance;
    
}

@end

如果仅仅是实现了sharedXxx方法,如果仍使用alloc init仍可以得到多个实例。

如果有两个线程同时实例化,很可能创建出两个实例来(是线程不安全的)。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。