atomic并不一定线程安全

319 阅读4分钟

举例子说明:

比如你开了一个服装店,这个服装店有三层,在第三层有个只能容纳一人试衣间,今天要来5个人买衣服,第一个人看上了一件衣服,要去试试,你告诉他位置他就去了,这时候试衣间的可容纳人数已经是0,这时候第二个人也要去试衣服,你也告诉他位置,他就去了,去了之后打开门,注意,这里能打开门,所以在他看来,这里是能进去的,但是打开门就尴尬了,然后这个时候两个人可能就干起来了,这个时候两个人(多线程)同时访问了这个试衣间(内存),造成了线程的不安全(容纳人数是这个内存中的数据),造成了错误,第一个人可能就说了,我已经先来了,把这块区域占了,已经在使用这个数据了,第二个人就会说,是老板跟我所试衣间在这里,我也要使用试衣间,我一拉门就开了,我以为里面没人。那么怎么解决这样的线程不安全呢?当然一看就知道,给这个门加个锁就行了,客户进来试衣服就把门锁上,第二个人来了,发现门打不开,就不会立即使用这个数据,等第一个人开锁出来了,第二个人再进去,这样数据就是安全的了,不会产生错误。关于加锁的位置也有说法,因为加锁是很耗资源的,所以锁的位置要放对,也不要多放,比如来买衣服,进来一个人你直接把服装店的大门就锁上了,就浪费了其他人选衣服的时间(要放对位置);第一个人选好了衣服,准备去试衣服,你直接把一楼,二楼,三楼,试衣间全锁上了,那这个人试玩衣服出来解完全部的锁,要花费大量的时间(浪费资源),当然这样也是没有必要的(不要多放)。只要在试衣间门上面上个锁就行了。

关于nonatomic和atomic也是一样,nonatomic线程不安全,atomic线程安全,具体表现在变量是setter方法有没有加锁。如果用atomic修饰就相当于每个人出生你就给他搞了个可携带式的加锁试衣间,不管干什么,都要先解锁,来买个衣服,先解了锁从试衣间出来跟你说话,说完又进去,解锁出来看衣服,看完再进去,很耗资源,所以建议我们使用nonatomic,先不装试衣间,只要在需要用的地方(比如你的服装店)搞个加锁的试衣间就可以

这个例子超级形象 哈哈哈哈!

所以Atomic 不能保证对象多线程的安全。它只是能保证你访问的时候给你返回一个完好无损的Value而已。

atomic和nonatomic的对比

1、atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。

2、atomic:系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。getter 还是能得到一个完好无损的对象(可以保证数据的完整性),但这个对象在多线程的情况下是不能确定的,比如上面的例子。

也就是说:如果有多个线程同时调用setter的话,不会出现某一个线程执行完setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁一样,每次只能有一个线程调用对象的setter方法,所以可以保证数据的完整性。

atomic所说的线程安全只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的。

3、nonatomic:就没有这个保证了,nonatomic返回你的对象可能就不是完整的value。因此,在多线程的环境下原子操作是非常必要的,否则有可能会引起错误的结果。但仅仅使用atomic并不会使得对象线程安全,我们还要为对象线程添加lock来确保线程的安全。

4、nonatomic的速度要比atomic的快。

5、atomic与nonatomic的本质区别其实也就是在setter方法上的操作不同

nonatomic对象setter和getter方法的实现:

  • (void)setCurrentImage:(UIImage *)currentImage { if (_currentImage != currentImage) { [_currentImage release]; _currentImage = [currentImage retain];

    } }

  • (UIImage *)currentImage { return _currentImage; }

atomic对象setter和getter方法的实现:

  • (void)setCurrentImage:(UIImage *)currentImage { @synchronized(self) { if (_currentImage != currentImage) { [_currentImage release]; _currentImage = [currentImage retain];

      }
    

    } }

  • (UIImage *)currentImage { @synchronized(self) { return _currentImage; } }