1.引
這是atomic的源碼,源碼地址點(diǎn) 這里
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
... ...
... ...
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
源碼里說明了 在getter和setter里添加了一把叫SpinLock的鎖,保證了setter和getter的線程安全
2.那么啥是SpinLock呢?
了解一下SpinLock
中文叫自旋鎖翰舌,當(dāng)一個(gè)線程獲得鎖之后,其他線程將會(huì)一直循環(huán)在哪里查看是否該鎖被釋放结啼。所以坎缭,此鎖比較適用于鎖的持有者保存時(shí)間較短的情況下。
在OC里它是OSSpinLock尚胞,OS為前綴說明它是MacOS的API
OSSpinLock
這里有一個(gè)問題硬霍,為什么蘋果使用自旋鎖,而不使用其它的鎖辐真?
賦值操作是一個(gè)非承肷校快速的操作,如果用互斥鎖的話侍咱,讓等待外面的線程休眠耐床,再醒來,是一件多么痛苦的事情啊楔脯,好不容易睡著了撩轰,睡沒幾毫秒就得起來,會(huì)有起床氣的!這里的起床氣意思是消耗CPU資源~
3.atomic安全嗎堪嫂?
這是一道經(jīng)典的面試題偎箫!-_-
看如下代碼
@property (atomic, assign) NSInteger plus;//有一個(gè)atomic的Int數(shù),用來自增
有一個(gè)自增的方法皆串,循環(huán)10次淹办,為了模擬線程搶奪的場景,我讓當(dāng)前線程睡1秒后執(zhí)行自增然后打印當(dāng)前線程以及自增后的值
- (void)plusMethod
{
for (NSInteger i = 0; i < 10; i++) {
sleep(1);
self.plus++;
NSLog(@"%@ plus:%ld", NSThread.currentThread, self.plus);
}
}
最后創(chuàng)建兩條線程去操作這個(gè)自增方法
- (void)gcd
{
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self plusMethod];
});
dispatch_async(queue, ^{
[self plusMethod];
});
}
想一下打印的結(jié)果恶复,是不是0怜森,1,2...19
打印的結(jié)果
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:2
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:2
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:4
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:3
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:5
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:6
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:8
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:8
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:9
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:10
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:11
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:12
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:13
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:14
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:15
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:15
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:16
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:17
<NSThread: 0x604000276080>{number = 3, name = (null)} plus:18
<NSThread: 0x60000047cc40>{number = 4, name = (null)} plus:18
結(jié)果的確是打印了20次谤牡,但是打印的結(jié)果并不是我們想象中的1~20副硅,也就是說 這段 代碼由線程安全的問題。
atomic并不是線程安全的翅萤,它只保證修飾的屬性的set和get在多線程調(diào)用下是安全的恐疲,但是并不能保證數(shù)據(jù)的線程安全,
解決方法:添加一把鎖
- (void)plusMethod
{
for (NSInteger i = 0; i < 10; i++) {
sleep(1);
OSSpinLockLock(&_spinLock);// <----
self.plus++;
NSLog(@"%@ plus:%ld", NSThread.currentThread, self.plus);
OSSpinLockUnlock(&_spinLock);// <-----
}
}
4.題外話
在Swift里套么,SpinLock是一個(gè)宏培己,它的值一把遞歸鎖~