iOS 鎖的底層分析(1)--@synchronized

前言

上一篇文章研究完了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片部、NSConditionNSRecursiveLock底層都是對(duì)pthread的封裝霜定。

  • 互斥和同步的理解

    • 互斥兩條線程處理档悠,同一時(shí)間只有一個(gè)線程可以運(yùn)行;
    • 同步:除了有互斥的意思外望浩,同時(shí)還有一定的順序要求辖所,即按照一定的順序執(zhí)行
  • 遞歸鎖
    就是同?個(gè)線程可以加鎖N次?不會(huì)引發(fā)死鎖磨德。
    注意:NSRecursiveLock缘回、@synchronizedpthread_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é)果

通過上面的運(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_enterobjc_sync_exit赴恨,根上面.cpp文件中的流程是一致的。

3.2 實(shí)現(xiàn)原理

libObjc.dylib源碼中分析其實(shí)現(xiàn)原理伴栓。搜索objc_sync_enterobjc_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_enterobjc_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)如下:

id2data

分析:
包含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)。見下圖:

拉鏈結(jié)構(gòu)

注意:一個(gè)SyncData對(duì)應(yīng)一個(gè)對(duì)象

@ synchronized的數(shù)據(jù)結(jié)構(gòu)

@ synchronized數(shù)據(jù)結(jié)構(gòu)圖

3.3 案例跟蹤分析

  • 單線程遞歸加鎖object不變
    案例分析

    跟蹤進(jìn)入斷點(diǎn)1104行)贷腕,跟蹤進(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ì)象),如下圖:

斷點(diǎn)跟蹤流程

繼續(xù)往下走,再次獲取當(dāng)前線程綁定的SyncData咖耘,此時(shí)不再為空翘簇,并且object相同。見下圖:
斷點(diǎn)跟蹤流程

線程綁定的SyncData對(duì)應(yīng)的object儿倒,與此時(shí)的object相同版保,再次創(chuàng)建鎖,并且鎖次數(shù)++夫否,見下圖:
斷點(diǎn)跟蹤流程

然后繼續(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森篷。見下圖:

斷點(diǎn)跟蹤流程

  • 單線程遞歸加鎖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)2objectperson2哼绑,此時(shí)線程已經(jīng)綁定了person1對(duì)應(yīng)的SyncData,所以線程綁定關(guān)系已經(jīng)被占用碉咆,但是object不相同抖韩。見下圖:

斷點(diǎn)跟蹤流程

因?yàn)?code>person2對(duì)象是第一次加鎖,所以線程對(duì)應(yīng)緩存列表listp中都沒有對(duì)應(yīng)的SyncData疫铜。見下圖:
斷點(diǎn)跟蹤流程

person2初次進(jìn)入茂浮,會(huì)進(jìn)行對(duì)象的創(chuàng)建,并將SyncData放入緩存列表中。見下圖:
斷點(diǎn)跟蹤流程

如果下次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)添加了person1person2對(duì)應(yīng)的SyncData植康,所以這里是可以獲取的旷太。并且會(huì)針多線程操作,從而是threadCount1销睁,此時(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)綁定了person1tls對(duì)應(yīng)的Object不相同钠至。

然后會(huì)從線程對(duì)應(yīng)的緩存列表中獲取葛虐,因?yàn)楫?dāng)前線程沒有添加過,所以這里查詢不到棉钧,最終會(huì)在listp中獲取對(duì)應(yīng)的SyncData屿脐。與此同時(shí)會(huì)進(jìn)行threadCount1操作。完成以上操作后掰盘,會(huì)將該SyncData添加到線程對(duì)應(yīng)的緩存列表中摄悯。見下圖:

斷點(diǎn)跟蹤流程

在新線程中的流程與外層線程的邏輯是一樣的,只是線程綁定的數(shù)據(jù)和緩存列表數(shù)據(jù)不一樣愧捕。

objc_sync_enter之后的流程圖
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艇拍。

      • enterlockCount++并存儲(chǔ)到SYNC_COUNT_DIRECT_KEY
      • exitlockCount--并存儲(chǔ)到SYNC_COUNT_DIRECT_KEY宠纯,lockCount == 0清空卸夕。
    • TLS cache緩存獲取,遍歷cache找到對(duì)應(yīng)的SyncData婆瓜。

      • enterlockCount++快集。
      • exitlockCount--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ù)。

      • enterTLS快速緩存不存在的情況下將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)用流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冤荆,一起剝皮案震驚了整個(gè)濱河市朴则,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件页滚,死亡現(xiàn)場(chǎng)離奇詭異贵扰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诽里,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拴魄,你說我怎么就攤上這事∠疲” “怎么了匹中?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)豪诲。 經(jīng)常有香客問我顶捷,道長(zhǎng),這世上最難降的妖魔是什么屎篱? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任服赎,我火速辦了婚禮葵蒂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘重虑。我一直安慰自己践付,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布缺厉。 她就那樣靜靜地躺著永高,像睡著了一般。 火紅的嫁衣襯著肌膚如雪提针。 梳的紋絲不亂的頭發(fā)上命爬,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音辐脖,去河邊找鬼饲宛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛揖曾,可吹牛的內(nèi)容都是我干的落萎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼炭剪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼练链!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奴拦,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤媒鼓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后错妖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绿鸣,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年暂氯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潮模。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痴施,死狀恐怖擎厢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辣吃,我是刑警寧澤动遭,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站神得,受9級(jí)特大地震影響厘惦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哩簿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一宵蕉、第九天 我趴在偏房一處隱蔽的房頂上張望酝静。 院中可真熱鬧,春花似錦国裳、人聲如沸形入。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浓若,卻和暖如春渺杉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挪钓。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工是越, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碌上。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓倚评,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親馏予。 傳聞我的和親對(duì)象是個(gè)殘疾皇子天梧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容