前言
上一篇文章研究完了GCD
相關(guān)的底層原理酷师,現(xiàn)在我們開始探索鎖的底層原理段只。眾所周知,鎖分為兩大類:自旋鎖
&互斥鎖
税稼。那么他們的工作原理是怎么樣子的呢?我們開發(fā)中怎么運(yùn)用這些鎖呢垮斯?拭目以待郎仆!
準(zhǔn)備工作
1. 鎖的歸類
1.1 自旋鎖
自旋鎖是一種用于保護(hù)多線程共享資源
的鎖,與一般互斥鎖
(mutex
)不同之處在于當(dāng)自旋鎖嘗試獲取鎖時(shí)以忙等待
(busy waiting
)的形式不斷地循環(huán)檢查鎖是否可用
兜蠕。當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住
)扰肌,那么下一個(gè)線程會(huì)一直等待
(不會(huì)睡眠
),當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢熊杨,下一個(gè)線程會(huì)立即執(zhí)行
曙旭。
注意:在多CPU
的環(huán)境中盗舰,對(duì)持有鎖較短的程序
來說,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能
桂躏。
自旋鎖:OSSpinLock(自旋鎖)
钻趋、讀寫鎖
1.2 互斥鎖
當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢
的時(shí)候(被鎖住
),那么下一個(gè)線程會(huì)進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢
剂习,當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢蛮位,下一個(gè)線程會(huì)自動(dòng)喚醒然后執(zhí)行任務(wù)
,該任務(wù)也不會(huì)立刻執(zhí)行
鳞绕,而是成為可執(zhí)行狀態(tài)
(就緒
)失仁。互斥鎖
(mutex
),?于保證在任何時(shí)刻们何,都只能有?個(gè)線程訪問該對(duì)象
萄焦。
mutex
函數(shù)
在Posix Thread
中定義有?套專??于線程同步的mutex函數(shù)
。mutex
垂蜗,?于保證在任何時(shí)刻楷扬,都只能有?個(gè)線程訪問該對(duì)象
解幽。當(dāng)獲取鎖操作失敗時(shí)贴见,線程會(huì)進(jìn)?睡眠
,等待鎖釋放時(shí)被喚醒
躲株。
注意:NSLock
片部、NSCondition
、NSRecursiveLock
底層都是對(duì)pthread
的封裝霜定。-
互斥和同步的理解
-
互斥
:兩條線程處理
档悠,同一時(shí)間只有一個(gè)線程
可以運(yùn)行; -
同步
:除了有互斥
的意思外望浩,同時(shí)還有一定的順序
要求辖所,即按照一定的順序執(zhí)行
。
-
遞歸鎖
就是同?個(gè)線程可以加鎖N次
?不會(huì)引發(fā)死鎖
磨德。
注意:NSRecursiveLock
缘回、@synchronized
、pthread_mutex(recursive)
是遞歸鎖
互斥鎖:pthread_mutex(互斥鎖)
典挑、@synchronized(互斥鎖)
酥宴、NSLock(互斥鎖)
、NSConditionLock(條件鎖)
您觉、NSCondition(條件鎖)
拙寡、NSRecursiveLock(遞歸鎖)
、dispatch_semaphore_t(信號(hào)量)
1.3 自旋鎖和互斥鎖的特點(diǎn)
-
自旋鎖會(huì)忙等
琳水,所謂忙等肆糕,即在訪問被鎖資源時(shí)般堆,調(diào)用者線程不會(huì)休眠
,而是不停循環(huán)在那里
诚啃,直到被鎖資源釋放鎖
郁妈。 -
互斥鎖會(huì)休眠
,所謂休眠绍申,即在訪問被鎖資源時(shí)噩咪,調(diào)用者線程會(huì)休眠
,此時(shí)cpu可以調(diào)度其他線程工作
极阅,直到被鎖資源釋放鎖胃碾。此時(shí)會(huì)喚醒休眠線程
。
1.3.1 自旋鎖優(yōu)缺點(diǎn)
-
優(yōu)點(diǎn)
:自旋鎖不會(huì)引起調(diào)用者睡眠
筋搏,所以不會(huì)進(jìn)行線程調(diào)度
仆百,CPU
時(shí)間片輪轉(zhuǎn)等耗時(shí)操作。所有如果能在很短的時(shí)間內(nèi)獲得鎖奔脐,自旋鎖的效率遠(yuǎn)高于互斥鎖
俄周。 -
缺點(diǎn)
:自旋鎖一直占用CPU
,他在未獲得鎖的情況下髓迎,一直運(yùn)行自旋
峦朗,所以占用著CPU
,如果不能在很短的時(shí)間內(nèi)獲得鎖排龄,這無疑會(huì)使CPU
效率降低波势。自旋鎖不能實(shí)現(xiàn)遞歸調(diào)用
。
1.4 鎖的性能
以下是鎖的性能圖橄维,同意條件下各種鎖的耗時(shí)尺铣,如下:
大部分鎖在
真機(jī)上
性能表現(xiàn)更好,@synchronized
在真機(jī)與模擬器中表現(xiàn)差異巨大
争舞。也就是說蘋果在真機(jī)模式下優(yōu)化了@synchronized
的性能凛忿。與之前相比目前@synchronized
的性能基本能滿足要求。
注意:判斷一把鎖的性能好壞
竞川,一般情況下是與pthread_mutex_t
做對(duì)比(因?yàn)榈讓佣际菍?duì)它的封裝)店溢。
2. 鎖的作用
通過一個(gè)案例進(jìn)行分析。模擬一個(gè)售票流程流译,總票數(shù)為20
張逞怨,有4
個(gè)窗口在同時(shí)進(jìn)行售票,實(shí)時(shí)跟蹤剩余票數(shù)
福澡。見下面代碼:
@interface ViewController ()
@property (nonatomic, assign) NSUInteger ticketCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.ticketCount = 20;
[self testSaleTicket];
}
- (void)testSaleTicket{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 3; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10; i++) {
[self saleTicket];
}
});
}
- (void)saleTicket{
if (self.ticketCount > 0) {
self.ticketCount--;
sleep(0.1);
NSLog(@"當(dāng)前余票還剩:%lu張",(unsigned long)self.ticketCount);
} else {
NSLog(@"當(dāng)前車票已售罄");
}
}
@end
運(yùn)行結(jié)果如下:
通過上面的運(yùn)行結(jié)果叠赦,發(fā)現(xiàn)因?yàn)?code>異步操作的原因,出現(xiàn)了
數(shù)據(jù)不安全問題
,數(shù)據(jù)出現(xiàn)了混亂
除秀。通常我們會(huì)通過加鎖
的方式來保證數(shù)據(jù)的安全
糯累,用來保證在任一時(shí)刻,只能有一個(gè)線程訪問該對(duì)象
册踩。
對(duì)上面的案例進(jìn)行修改如下:
添加一個(gè)
@synchronized
互斥鎖泳姐,重新運(yùn)行程序,發(fā)現(xiàn)其能夠正常運(yùn)行暂吉,并能夠保證數(shù)據(jù)的安全性
胖秒。@synchronized用著更方便,可讀性更高慕的,也是我們最常用的
阎肝。當(dāng)然一些小伙伴說也可以用信號(hào)量
來控制啊,別忘了信號(hào)量也是互斥鎖
肮街。
3. @synchronized實(shí)現(xiàn)原理
通過上面的案例我們了解到了鎖的作用风题,那么@synchronized
到底做了什么工作呢?這是我們所需要研究分析的嫉父。
3.1 底層探索
-
通過使用
xcrun
生成.cpp
文件查看底層原理
提供以下的代碼沛硅,如下:
代碼示例
xcrun
之后生成.cpp
文件,打開.cpp
文件绕辖,定位到main
函數(shù)對(duì)應(yīng)的位置摇肌。見下圖:
man函數(shù)定位
可以看到,調(diào)用了objc_sync_enter
方法引镊,并且使用了try-catch
朦蕴,在正常處理流程中,提供了_SYNC_EXIT
結(jié)構(gòu)體弟头,最后也會(huì)調(diào)用對(duì)應(yīng)的析構(gòu)函數(shù)objc_sync_exit
。 -
打開匯編斷點(diǎn)涉茧,查看匯編流程
匯編斷點(diǎn)
通過匯編我們可以發(fā)現(xiàn)底層調(diào)用了兩個(gè)方法分別是objc_sync_enter
和objc_sync_exit
赴恨,根上面.cpp
文件中的流程是一致的。
3.2 實(shí)現(xiàn)原理
在libObjc.dylib
源碼中分析其實(shí)現(xiàn)原理伴栓。搜索objc_sync_enter
和objc_sync_exit
兩個(gè)方法的源碼實(shí)現(xiàn):
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, RELEASE);
if (!data) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
bool okay = data->mutex.tryUnlock();
if (!okay) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {
// @synchronized(nil) does nothing
}
return result;
}
由源碼可以看出伦连,objc_sync_enter
與objc_sync_exit
的方法流程都是一一對(duì)應(yīng)的。
流程分析:
- 首先加鎖和解鎖都會(huì)對(duì)
obj
進(jìn)行判斷钳垮,如果obj
為空惑淳,什么也沒有做,在libObjc.dylib
源碼中饺窿,沒有查到objc_sync_nil()
的相關(guān)實(shí)現(xiàn)歧焦。 - 如果
obj
不為空,在enter
方法中肚医,會(huì)封裝一個(gè)SyncData
對(duì)象绢馍,并對(duì)調(diào)用mutex
屬性進(jìn)行上鎖lock()
向瓷;在exit
方法時(shí),同樣獲取對(duì)應(yīng)的SyncData
對(duì)象舰涌,然后調(diào)用data->mutex.tryUnlock();
進(jìn)行解鎖猖任。
SyncData結(jié)構(gòu)分析
查看源碼,發(fā)現(xiàn)SyncData
的定義如下:
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;
DisguisedPtr<objc_object> object;
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex;
} SyncData;
-
struct SyncData* nextData;
包含了一個(gè)相同的數(shù)據(jù)結(jié)構(gòu)瓷耙,說明它是一個(gè)單向鏈表結(jié)構(gòu)
-
object
使用DisguisedPtr
進(jìn)行了包裝 -
threadCount
線程的數(shù)量朱躺,有多少個(gè)線程對(duì)該對(duì)象進(jìn)行加鎖
recursive_mutex_t mutex;遞歸鎖
初步判斷:@synchronized
支持遞歸鎖
,并且支持多線程訪問
搁痛。
id2data
方法
id2data
方法實(shí)現(xiàn)如下:
分析:
包含
3
個(gè)大步驟室琢,首先通過tls
,從線程緩存
中獲取當(dāng)前線程的SyncData
進(jìn)行相關(guān)處理落追;如果緩存中存在對(duì)應(yīng)的SyncData
則從緩存中獲取并處理盈滴;最后包括一些內(nèi)部的初始化插入緩存等操作。(詳細(xì)的步驟在后面案例通過lldb
進(jìn)行分析)
LOCK_FOR_OBJ&LIST_FOR_OBJ
查看兩者的宏定義如下:
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;
StripedMap
的結(jié)構(gòu)分析
首先查看StripedMap
的定義如下:
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 }; //表的size
#else
enum { StripeCount = 64 };
#endif
給表為不同的架構(gòu)環(huán)境提供了不同的容量轿钠,真機(jī)環(huán)境的容量為8
巢钓,模擬環(huán)境的容量為64
。而其元素為SyncList
疗垛,SyncList
的數(shù)據(jù)結(jié)構(gòu)為:
struct SyncList {
SyncData *data;
spinlock_t lock;
constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};
而SyncData
是一個(gè)鏈表結(jié)構(gòu)
症汹,至此形成了一個(gè)拉鏈結(jié)構(gòu)
。見下圖:
注意:一個(gè)SyncData對(duì)應(yīng)一個(gè)對(duì)象
@ synchronized
的數(shù)據(jù)結(jié)構(gòu)
3.3 案例跟蹤分析
-
單線程遞歸加鎖
object
不變
案例分析
跟蹤進(jìn)入斷點(diǎn)1
(104
行)贷腕,跟蹤進(jìn)入id2data
方法背镇。此時(shí)StripedMap
表中64
個(gè)數(shù)據(jù)全是空。見下圖:
內(nèi)部斷點(diǎn)流程
繼續(xù)跟蹤調(diào)試泽裳,會(huì)調(diào)用tls_get_direct
方法瞒斩,獲取當(dāng)前線程綁定的SyscData
,因?yàn)槭堑谝淮芜M(jìn)行加鎖
涮总,所以這里的data
是空胸囱。見下圖:
內(nèi)部斷點(diǎn)流程
緊接著會(huì)從當(dāng)前線程的緩存列表
中獲取對(duì)應(yīng)的SyncData
,很顯然此時(shí)緩存中也沒有存儲(chǔ)該對(duì)象
瀑梗,所以此時(shí)也是空
烹笔。見下圖:
內(nèi)部斷點(diǎn)流程
當(dāng)前線程綁定的SyncData
和線程對(duì)應(yīng)的緩存列表
中的SyncData
都為空
,則會(huì)從哈希表
中獲取抛丽,當(dāng)前的表中也沒有對(duì)應(yīng)的數(shù)據(jù)谤职,見下圖:
內(nèi)部斷點(diǎn)流程
上面三個(gè)地方都沒有找到對(duì)應(yīng)的SyncData
,最終會(huì)創(chuàng)建一個(gè)SyncData
亿鲜,并采用頭插法
將數(shù)據(jù)插入到對(duì)應(yīng)listp頭部
允蜈。見下圖:
插入數(shù)據(jù)流程
完成SyncData
創(chuàng)建后,會(huì)綁定到當(dāng)前線程上
(一個(gè)線程只會(huì)綁定一個(gè),并且綁定后不再改變
)陷寝,注意此時(shí)并沒有保存到線程對(duì)應(yīng)的緩存列表中
锅很。見下圖:
綁定線程
最后返回result
,完成加鎖功能
凤跑。
然后進(jìn)入斷點(diǎn)2
爆安,查看第二次加鎖
的流程,進(jìn)入id2data
方法仔引,此時(shí)哈希表中已經(jīng)有一個(gè)數(shù)據(jù)
扔仓,也就是此時(shí)對(duì)象對(duì)應(yīng)的listp
此時(shí)也不再為空(同一個(gè)對(duì)象),如下圖:
繼續(xù)往下走,再次獲取當(dāng)前線程綁定的
SyncData
咖耘,此時(shí)不再為空
翘簇,并且object
相同。見下圖:線程綁定的
SyncData
對(duì)應(yīng)的object
儿倒,與此時(shí)的object
相同版保,再次創(chuàng)建鎖,并且鎖次數(shù)++
夫否,見下圖:然后繼續(xù)進(jìn)入斷點(diǎn)3
彻犁,進(jìn)行第三次加鎖
時(shí),因?yàn)榇藭r(shí)object
沒有發(fā)生改變凰慈,線程也沒有改變汞幢,此時(shí)哈希表依然是一個(gè)元素
,同時(shí)對(duì)應(yīng)的listp
也只有一個(gè)元素
微谓,此時(shí)上鎖此時(shí)會(huì)變?yōu)?code>3森篷。見下圖:
-
單線程遞歸加鎖
object
變化
引入下面這個(gè)案例,我們直接從第二個(gè)斷點(diǎn)
開始分析豺型,見下圖:
斷點(diǎn)跟蹤流程
第一個(gè)斷點(diǎn)的流程分析已經(jīng)在上面的案例分析了仲智,這里就不再做分析。此時(shí)會(huì)創(chuàng)建一個(gè)新的SyncData
触创,并且會(huì)綁定到當(dāng)前線程
中坎藐。
進(jìn)入斷點(diǎn)2
,object
為person2
哼绑,此時(shí)線程已經(jīng)綁定了person1
對(duì)應(yīng)的SyncData
,所以線程綁定關(guān)系已經(jīng)被占用
碉咆,但是object
不相同抖韩。見下圖:
因?yàn)?code>person2對(duì)象是
第一次加鎖
,所以線程對(duì)應(yīng)緩存列表
和listp
中都沒有對(duì)應(yīng)的SyncData
疫铜。見下圖:person2
初次進(jìn)入茂浮,會(huì)進(jìn)行對(duì)象的創(chuàng)建,并將SyncData
放入緩存列表
中。見下圖:如果下次
person2
再次加鎖時(shí)席揽,會(huì)從緩存列表中獲取
顽馋。而如果person1
再次加鎖,會(huì)從當(dāng)前線程
中獲取幌羞,因?yàn)楫?dāng)前線程已經(jīng)綁定了person1對(duì)應(yīng)的SyncData
寸谜。
-
多線程遞歸加鎖
object
變化
引入下面的案例,見下圖:
案例分析
上面案例中属桦,前兩個(gè)加鎖過程這里不再分析熊痴,和上面單線程是一樣的,我們從多線程
時(shí)開始分析聂宾,也就是第113
行開始果善。
從斷點(diǎn)1
處進(jìn)行跟蹤,進(jìn)入id2data
方法系谐,此時(shí)哈希表
中的數(shù)據(jù)個(gè)數(shù)為2
巾陕,也就是外層線程
添加的兩個(gè)SyncData
。見下圖:
斷點(diǎn)跟蹤流程
繼續(xù)跟蹤代碼纪他,從線程中獲取其綁定的SyncData
鄙煤,此時(shí)為NULL
,因?yàn)槭切碌木€程止喷,還沒有加過鎖馆类,所以綁定數(shù)據(jù)為空
,fastCacheOccupied=NO
弹谁。見下圖:
斷點(diǎn)跟蹤流程
從緩存列表中獲取對(duì)應(yīng)的SyncData
乾巧,也是NULL
,所以這里的緩存列表也是和線程一一對(duì)應(yīng)
的预愤。見下圖:
斷點(diǎn)跟蹤流程
最后會(huì)從listp
中獲取對(duì)應(yīng)的數(shù)據(jù)沟于,在外層線程中,已經(jīng)添加了person1
和person2
對(duì)應(yīng)的SyncData
植康,所以這里是可以獲取的旷太。并且會(huì)針多線程操作,從而是threadCount
加1
销睁,此時(shí)對(duì)應(yīng)的線程數(shù)會(huì)變成2
供璧,見下圖:
斷點(diǎn)跟蹤流程
獲取數(shù)據(jù)后,因?yàn)榍懊?code>fastCacheOccupied=NO冻记,則會(huì)將該SyncData綁定到當(dāng)前這個(gè)線程
睡毒,也就是每個(gè)線程都會(huì)默認(rèn)綁定第一個(gè)object
,見下圖:
斷點(diǎn)跟蹤流程
進(jìn)入斷點(diǎn)2冗栗,進(jìn)行person2
的加鎖操作演顾,此時(shí)首先會(huì)獲取當(dāng)前線程綁定的SyncData
供搀,因?yàn)榇藭r(shí)已經(jīng)綁定了person1
,tls
對(duì)應(yīng)的Object
不相同钠至。
然后會(huì)從線程對(duì)應(yīng)的緩存列表
中獲取葛虐,因?yàn)楫?dāng)前線程沒有添加過
,所以這里查詢不到棉钧,最終會(huì)在listp
中獲取對(duì)應(yīng)的SyncData
屿脐。與此同時(shí)會(huì)進(jìn)行threadCount
加1
操作。完成以上操作后掰盘,會(huì)將該SyncData
添加到線程對(duì)應(yīng)的緩存列表
中摄悯。見下圖:
在新線程中的流程與外層線程的邏輯是一樣的,只是線程綁定的數(shù)據(jù)和緩存列表數(shù)據(jù)不一樣愧捕。
objc_sync_enter之后的流程圖
3.4 @synchronized原理總結(jié)
參數(shù)傳
nil
沒有做任何事情奢驯。傳self
在使用過程中不會(huì)被釋放
,并且同一個(gè)類
中如果都用self
底層只會(huì)存在一個(gè)SynData
次绘。@synchronized
底層是封裝的os_unfair_lock
瘪阁。objc_sync_enter
中加鎖,objc_sync_exit
中解鎖邮偎。@synchronized
加鎖的數(shù)據(jù)信息都存儲(chǔ)在sDataLists全局哈希表
中管跺。同時(shí)還有TLS
快速緩(一個(gè)SynData數(shù)據(jù),通常是第一個(gè)禾进,釋放后會(huì)存放新的
)以及線程緩存(緩存跟線程一一對(duì)應(yīng)的豁跑,緩存之間是互斥關(guān)系
)-
id2data
獲取SynData
流程:-
TLS
快速緩存獲取(SYNC_COUNT_DIRECT_KEY
)泻云,obj
對(duì)應(yīng)的SyncData
存在的情況下獲取SYNC_COUNT_DIRECT_KEY
對(duì)應(yīng)的lockCount
艇拍。-
enter
:lockCount++
并存儲(chǔ)到SYNC_COUNT_DIRECT_KEY
。 -
exit
:lockCount--
并存儲(chǔ)到SYNC_COUNT_DIRECT_KEY
宠纯,lockCount == 0
清空卸夕。
-
-
TLS cache
緩存獲取,遍歷cache
找到對(duì)應(yīng)的SyncData
婆瓜。-
enter
:lockCount++
快集。 -
exit
:lockCount--
。lockCount == 0
替換cache->list
對(duì)應(yīng)的值為最后一個(gè)廉白,used -1
个初,threadCount -1
。
-
sDataLists
全局哈希表獲取SyncData
:找到的情況下threadCount + 1
進(jìn)入緩存
邏輯猴蹂,沒有找到并且存在threadCount = 0
則替換object
相當(dāng)于存儲(chǔ)了新值勃黍。SyncData
創(chuàng)建:創(chuàng)建SyncData
,賦值object
晕讲,threadCount
初始化為1
覆获,創(chuàng)建mutex鎖
。并且采用頭插法
將SyncData插入sDataLists對(duì)應(yīng)的SynList頭部
瓢省。-
SyncData
數(shù)據(jù)緩存:sDataLists
添加了或者更新了數(shù)據(jù)會(huì)走到緩存邏輯弄息,緩存邏輯是往TLS快速緩存
以及TLS cache緩存
添加數(shù)據(jù)。-
enter
:TLS快速緩
存不存在的情況下將SyncData
存儲(chǔ)快速緩存
勤婚,否則存入cache
緩存的尾部
摹量。 -
exit
:直接return
。
-
-
-
lockCount
是針對(duì)單個(gè)線程
而言的馒胆,當(dāng)lockCount = 0
的時(shí)候?qū)?shù)據(jù)進(jìn)行釋放
缨称。-
TLS
快速緩存是直接設(shè)置為NULL
(只有一個(gè)SyncData
)。 -
TLS cache
緩存是直接用最后一個(gè)
數(shù)據(jù)進(jìn)行替換(一組SyncData
)祝迂,然后used -1
進(jìn)行釋放
睦尽。 - 同時(shí)
threadCount - 1
相當(dāng)于當(dāng)前線程被釋放
。
-
threadCount
是針對(duì)跨線程
的型雳,在threadCount = 0
的時(shí)候并不立即釋放
当凡,而是在下次插入數(shù)據(jù)的時(shí)候進(jìn)行替換
。sDataLists
保存所有的數(shù)據(jù)
纠俭。lockCount
是@synchronized
可重入可遞歸的原因
沿量,threadCount
是@synchronized
可跨線程的原因
。-
@synchronized
數(shù)據(jù)之間關(guān)系:
@synchronized數(shù)據(jù)之間關(guān)系 -
@synchronized
完整調(diào)用流程:
@synchronized完整調(diào)用流程