第十四篇:iOS鎖

自旋鎖勋乾,互斥鎖杨何,讀寫鎖
自旋鎖就是一個(gè)忙等狀態(tài) do--while
互斥鎖就是一個(gè)閑等糕篇,可以使得CPU進(jìn)行休眠去做別的處理

下面是iOS中所用到的鎖:執(zhí)行10萬次所用的時(shí)間

OSSpinLock:                   1.70 ms  自旋鎖  iOS10后被移除火诸,因?yàn)槠溆袃?yōu)先級(jí)占用躲查,蘋果用os_unfair_lock這個(gè)互斥鎖來代替被廢除的OSSpinLock鎖

dispatch_semaphore:           3.19 ms
pthread_mutex:                2.18 ms
NSCondition:                  3.33 ms
NSLock:                       2.93 ms
pthread_mutex(recursive):     3.08 ms
NSRecursiveLock:              4.66 ms
NSConditionLock:             10.82 ms
@synchronized:                9.09 ms

下面代碼打印后它浅,我們發(fā)現(xiàn)其并不是以49依次遞減的,這說明self.count這個(gè)是線程不安全的镣煮。如果改成atomic原子屬性姐霍,其也不會(huì)保證線程安全的。這個(gè)是因?yàn)? self.count --這句代碼的執(zhí)行,其實(shí)就是執(zhí)行了set和get方法镊折,這樣同時(shí)執(zhí)行原子屬性無法同時(shí)滿足線程安全黔衡,只滿足了一邊的線程安全。

解決這個(gè)問題就需要用到鎖功能腌乡。

@property (nonatomic ,assign) int count;

 self.count = 50;

- (void)test {
    for (int i = 0; i < 10; i ++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.count --;
            NSLog(@"%d",self.count);
        });
    }
}
2022-05-31 15:11:04.921282+0800 iOSLockDemo[4510:100720] 49
2022-05-31 15:11:04.921301+0800 iOSLockDemo[4510:100719] 47
2022-05-31 15:11:04.921291+0800 iOSLockDemo[4510:100732] 48
2022-05-31 15:11:04.921311+0800 iOSLockDemo[4510:100727] 46
2022-05-31 15:11:04.921334+0800 iOSLockDemo[4510:100733] 45
2022-05-31 15:11:04.921342+0800 iOSLockDemo[4510:100720] 44
2022-05-31 15:11:04.921355+0800 iOSLockDemo[4510:100734] 43
2022-05-31 15:11:04.921397+0800 iOSLockDemo[4510:100732] 42
2022-05-31 15:11:04.921410+0800 iOSLockDemo[4510:100719] 41
2022-05-31 15:11:04.921458+0800 iOSLockDemo[4510:100724] 40

下面是set屬性源碼:

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 (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {//這里如果是非原子操作就直接賦值處理
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];//如果是原子操作就加一把spinlock_t鎖
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}

下面是get屬性方法源碼:

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];//是原子就會(huì)加spinlock_t把鎖
    slotlock.lock();//加鎖
    id value = objc_retain(*slot);
    slotlock.unlock();//解鎖
    
    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}

os_unfair_lock互斥鎖

當(dāng)加了os_unfair_lock鎖后發(fā)現(xiàn)其打印就是按順序進(jìn)行打印了

- (void)test {
    for (int i = 0; i < 10; i ++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self unfairLock_test];
        });
    }
}

-(void)unfairLock_test {
    os_unfair_lock_lock(&_unfairLock);
    self.count --;
    NSLog(@"%d",self.count);
    os_unfair_lock_unlock(&_unfairLock);
}

2022-05-31 16:38:19.268185+0800 iOSLockDemo[6256:161713] 49
2022-05-31 16:38:19.268249+0800 iOSLockDemo[6256:161718] 48
2022-05-31 16:38:19.268320+0800 iOSLockDemo[6256:161718] 47
2022-05-31 16:38:19.268413+0800 iOSLockDemo[6256:161713] 46
2022-05-31 16:38:19.268449+0800 iOSLockDemo[6256:161715] 45
2022-05-31 16:38:19.268492+0800 iOSLockDemo[6256:161712] 44
2022-05-31 16:38:19.268539+0800 iOSLockDemo[6256:161726] 43
2022-05-31 16:38:19.268621+0800 iOSLockDemo[6256:161717] 42
2022-05-31 16:38:19.268733+0800 iOSLockDemo[6256:161725] 41
2022-05-31 16:38:19.268998+0800 iOSLockDemo[6256:161719] 40
WechatIMG2017.jpeg

NSLock鎖

其是對(duì)Pthreds的封裝

- (void)test {
    for (int i = 0; i < 10; i ++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self nslock_test];
        });
    }
}

-(void)nslock_test {
    [self.iLock lock];
    self.count --;
    NSLog(@"%d",self.count);
    [self.iLock unlock];
}
WechatIMG2018.jpeg

NSConditon鎖

其是遵循了NSLocking協(xié)議,所以其有l(wèi)ock和unlock方法夜牡。

下面代碼可以模擬一個(gè)搶票的機(jī)制

- (void)nscondition_test {
    for (int i = 0; i < 50; i ++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self lg_production];
        });
    }
    for (int i = 0; i < 100; i ++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self lg_consumption];
        });
    }
}

- (void)lg_production {
    [self.iCondition lock];
    self.count ++;
    NSLog(@"生產(chǎn)了一個(gè)產(chǎn)品,現(xiàn)有產(chǎn)品 : %d個(gè)",self.count);
    [self.iCondition signal];//喚醒
    [self.iCondition unlock];
}

- (void)lg_consumption {//這里依然會(huì)有負(fù)數(shù)打印与纽,這個(gè)是因?yàn)槭窍到y(tǒng)的bug,虛假喚醒
    [self.iCondition lock];
    while (self.count == 0) {//等待塘装,如果沒有這個(gè)會(huì)打印負(fù)數(shù)急迂,用while就會(huì)避免虛假喚醒
        [self.iCondition wait];
    }
    self.count --;
    NSLog(@"消費(fèi)了一個(gè)產(chǎn)品,現(xiàn)有產(chǎn)品: %d個(gè)",self.count);
    [self.iCondition unlock];
}

WechatIMG2019.jpeg

NSConditonLock鎖

其是對(duì)NSConditon的又一次封裝,通過下面的鎖蹦肴,可以使得線程按1,2,3順序打印

- (void)lg_testConditonLock{
    
    self.iConditionLock = [[NSConditionLock alloc] initWithCondition:3];//3個(gè)線程鎖

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self.iConditionLock lockWhenCondition:3];//加鎖
        NSLog(@"線程 1");
        [self.iConditionLock unlockWithCondition:2];//解鎖變成2
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self.iConditionLock lockWhenCondition:2];
        NSLog(@"線程 2");
        [self.iConditionLock unlockWithCondition:1];
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self.iConditionLock lockWhenCondition:1];
        NSLog(@"線程 3");
        [self.iConditionLock unlockWithCondition:0];
    });
}
WechatIMG2020.jpeg

NSRecursiveLock鎖

這個(gè)是個(gè)遞歸鎖僚碎,這個(gè)只能保證在同一個(gè)線程里操作

鎖在同一時(shí)刻只能被一條線程擁有
遞歸鎖同一時(shí)刻能被多條線程鎖擁有

-(void)recursiveLock_test {
    
    [self.iRecursiveLock lock];
    self.count --;
    NSLog(@"%d",self.count);
    [self.iRecursiveLock unlock];
}

- (void)test {
    for (int i = 0; i < 10; i ++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self recursiveLock_test];
        });
    }
}

- (void)recursiveTest {
 
如果這里加了for循環(huán)進(jìn)行幾次,每次調(diào)用下面的代碼就會(huì)出現(xiàn)奔潰阴幌。這里是因?yàn)榧恿薴or就會(huì)有多個(gè)線程勺阐,遞歸鎖同一時(shí)刻能被多條線程鎖擁有,但是在解鎖的時(shí)候沒有按順序就會(huì)奔潰矛双,如果要解決這個(gè)問題可以添加 @synchronized這個(gè)鎖就可以解決渊抽。


    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        static void (^recursiveMethod)(int);
        recursiveMethod = ^(int value){//block的遞歸調(diào)用,遞歸操作需要用到self.iRecursiveLock這種遞歸鎖
            if (value > 0) {
                [self.iRecursiveLock lock];
                NSLog(@"%d",value);
                recursiveMethod(value - 1);
                [self.iRecursiveLock unlock];
            }
        };
        recursiveMethod(10);
    });
}

WechatIMG2021.jpeg

pthread_mutex鎖

其是c語言底層寫的

- (void)lg_pthread_mutex {
    
    //非遞歸
    pthread_mutex_t lock0;
    pthread_mutex_init(&lock0, NULL);
    pthread_mutex_lock(&lock0);
    pthread_mutex_unlock(&lock0);
    pthread_mutex_destroy(&lock0);
    
    //遞歸
    pthread_mutex_t lock;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&lock, &attr);
    pthread_mutexattr_destroy(&attr);
    pthread_mutex_lock(&lock);
    pthread_mutex_unlock(&lock);
    pthread_mutex_destroy(&lock);
}
WechatIMG2022.jpeg

讀寫鎖

讀和寫互斥议忽,多讀單寫懒闷,下面我們用GCD來實(shí)現(xiàn)

- (NSString *)lg_read {
    // 異步讀取
    __block NSString *ret;
    dispatch_sync(self.iQueue, ^{
        // 讀取的代碼
        ret = self.dataDic[@"name"];
    });
    NSLog(@"%@",ret);
    return ret;
}

- (void)lg_write: (NSString *)name {
    // 寫操作
    dispatch_barrier_async(self.iQueue, ^{
        [self.dataDic setObject:name forKey:@"name"];
    });
}

@synchronized鎖

首先在main函數(shù)里添加 @synchronized (obj) 這個(gè)方法,然后用clang命令可以打印出main.cpp文件栈幸。

#import <Cocoa/Cocoa.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        NSObject *obj = [NSObject alloc];
        @synchronized (obj) {
            
        }
    }
    return NSApplicationMain(argc, argv);
}

下面是得到的cpp

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));
        { id _rethrow = 0; id _sync_obj = (id)obj; objc_sync_enter(_sync_obj);
try {
    struct _SYNC_EXIT {
    _SYNC_EXIT(id arg) : sync_exit(arg) {}
    ~_SYNC_EXIT() {objc_sync_exit(sync_exit);}//~_SYNC_EXIT() 是一個(gè)析構(gòu)函數(shù)
    id sync_exit;
    }
    
    _sync_exit(_sync_obj);//結(jié)構(gòu)體_SYNC_EXIT的構(gòu)造函數(shù)

        } catch (id e) {_rethrow = e;}
{ struct _FIN { _FIN(id reth) : rethrow(reth) {}
    ~_FIN() { if (rethrow) objc_exception_throw(rethrow); }
    id rethrow;
    } _fin_force_rethow(_rethrow);}
}

    }
    return NSApplicationMain(argc, argv);
}

其實(shí)在synchronized里有用到下面兩個(gè)函數(shù)愤估,objc_sync_exit和objc_sync_enter。這里的SyncData是一個(gè)單向的鏈表速址。其中其里面有個(gè)spinlock_t玩焰,這個(gè)其實(shí)就是 os_unfair_lock鎖。

WechatIMG2027.jpeg
static SyncData* id2data(id object, enum usage why)
{
    spinlock_t *lockp = &LOCK_FOR_OBJ(object);
    SyncData **listp = &LIST_FOR_OBJ(object);
    SyncData* result = NULL;

#if SUPPORT_DIRECT_THREAD_KEYS
    // Check per-thread single-entry fast cache for matching object
    bool fastCacheOccupied = NO;
    SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
    if (data) {
        fastCacheOccupied = YES;

        if (data->object == object) {
            // Found a match in fast cache.
            uintptr_t lockCount;

            result = data;
            lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
         

下面的StripedMap是用來緩存帶spinlock鎖能力的類或者結(jié)構(gòu)體壳繁,如果一張表存儲(chǔ)一個(gè)那么會(huì)浪費(fèi)內(nèi)存震捣,如果一張表存儲(chǔ)多個(gè)那么效率會(huì)很低,所以就執(zhí)行設(shè)置特定的個(gè)數(shù)StripeCount = 8(非模擬器上) 或者StripeCount = 64 (模擬器上)闹炉。其實(shí)這個(gè)SyncList里是存著sDataLists這樣的數(shù)據(jù)形式蒿赢。

在線程的局部存儲(chǔ)空間里也就是TLS,其里面緩存有synchronized的信息.也就是線程里有單獨(dú)開辟了一小部分空間讓其存儲(chǔ)一些關(guān)于線程的數(shù)據(jù)渣触。

static StripedMap<SyncList> sDataLists;

template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif
static SyncCache *fetch_cache(bool create)
{
    _objc_pthread_data *data;
    
    data = _objc_fetch_pthread_data(create);
    if (!data) return NULL;

    if (!data->syncCache) {
        if (!create) {
            return NULL;
        } else {
            int count = 4;
            data->syncCache = (SyncCache *)
                calloc(1, sizeof(SyncCache) + count*sizeof(SyncCacheItem));
            data->syncCache->allocated = count;
        }
    }

下面是_objc_pthread_data 這個(gè)數(shù)據(jù)結(jié)構(gòu)體里存syncCache信息羡棵,這里就是關(guān)于synchronize的信息。


typedef struct {
    struct _objc_initializing_classes *initializingClasses; // for +initialize
    struct SyncCache *syncCache;  // for @synchronize
    struct alt_handler_list *handlerList;  // for exception alt handlers
    char *printableNames[4];  // temporary demangled names for logging
    const char **classNameLookups;  // for objc_getClass() hooks
    unsigned classNameLookupsAllocated;
    unsigned classNameLookupsUsed;

    // If you add new fields here, don't forget to update 
    // _objc_pthread_destroyspecific()

} _objc_pthread_data;

下面SyncCache里是存著SyncCacheItem嗅钻,在SyncCacheItem里有l(wèi)ockCount皂冰,這個(gè)是記錄當(dāng)前線程的加鎖次數(shù)

typedef struct {
    SyncData *data;
    unsigned int lockCount;  // number of times THIS THREAD locked this block
} SyncCacheItem;

下面是加鎖的時(shí)候lockCount會(huì)加1店展,解鎖的時(shí)候lockCount會(huì)減1,下面的源碼也就是在線程的緩存里去找和我們這個(gè) 加鎖的對(duì)象SyncData秃流,找到的話就會(huì)返回赂蕴。這里有快速的緩存,去查找拿取舶胀,沒有的話就去objc_pthresd_data里正常查找概说。如果還沒有就會(huì)去表里找(StripeCount數(shù)量表中),如果還沒有那就會(huì)創(chuàng)建一個(gè)添加到表里嚣伐。

static SyncData* id2data(id object, enum usage why)
{
    spinlock_t *lockp = &LOCK_FOR_OBJ(object);
    SyncData **listp = &LIST_FOR_OBJ(object);
    SyncData* result = NULL;

#if SUPPORT_DIRECT_THREAD_KEYS
    // Check per-thread single-entry fast cache for matching object
    bool fastCacheOccupied = NO;
    SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
    if (data) {
        fastCacheOccupied = YES;

        if (data->object == object) {
            // Found a match in fast cache.
            uintptr_t lockCount;

            result = data;
            lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
            if (result->threadCount <= 0  ||  lockCount <= 0) {
                _objc_fatal("id2data fastcache is buggy");
            }

            switch(why) {
            case ACQUIRE: {
                lockCount++;
                tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
                break;
            }
            case RELEASE:
                lockCount--;
                tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
                if (lockCount == 0) {
                    // remove from fast cache
                    tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
                    // atomic because may collide with concurrent ACQUIRE
                    OSAtomicDecrement32Barrier(&result->threadCount);
                }
                break;
            case CHECK:
                // do nothing
                break;
            }

            return result;
        }
    }
#endif

下面用一個(gè)圖示來顯示整個(gè)過程:
其中l(wèi)ockcount是指當(dāng)前線程的加鎖數(shù)糖赔,thredcount是指當(dāng)前鎖被幾條線程所擁有。

WechatIMG2033.jpeg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轩端,一起剝皮案震驚了整個(gè)濱河市放典,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌基茵,老刑警劉巖奋构,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拱层,居然都是意外死亡声怔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門舱呻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來醋火,“玉大人,你說我怎么就攤上這事箱吕〗娌担” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵茬高,是天一觀的道長(zhǎng)兆旬。 經(jīng)常有香客問我,道長(zhǎng)怎栽,這世上最難降的妖魔是什么丽猬? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮熏瞄,結(jié)果婚禮上脚祟,老公的妹妹穿的比我還像新娘。我一直安慰自己强饮,他們只是感情好由桌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般行您。 火紅的嫁衣襯著肌膚如雪铭乾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天娃循,我揣著相機(jī)與錄音炕檩,去河邊找鬼。 笑死捌斧,一個(gè)胖子當(dāng)著我的面吹牛捧书,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骤星,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼爆哑!你這毒婦竟也來了洞难?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤揭朝,失蹤者是張志新(化名)和其女友劉穎队贱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潭袱,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柱嫌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屯换。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片编丘。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖彤悔,靈堂內(nèi)的尸體忽然破棺而出嘉抓,到底是詐尸還是另有隱情,我是刑警寧澤晕窑,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布抑片,位于F島的核電站,受9級(jí)特大地震影響杨赤,放射性物質(zhì)發(fā)生泄漏敞斋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一疾牲、第九天 我趴在偏房一處隱蔽的房頂上張望植捎。 院中可真熱鬧,春花似錦阳柔、人聲如沸鸥跟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽医咨。三九已至枫匾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拟淮,已是汗流浹背干茉。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留很泊,地道東北人角虫。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像委造,于是被迫代替她去往敵國(guó)和親戳鹅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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

  • iOS之武功秘籍 文章匯總[http://www.reibang.com/p/07991e5b1c30] 寫在前...
    長(zhǎng)茳閱讀 586評(píng)論 0 2
  • 蘋果官方資源opensource[https://opensource.apple.com/releases/]o...
    頂級(jí)蝸牛閱讀 812評(píng)論 0 7
  • 寫在前面 多線程在日常開發(fā)中能起到性能優(yōu)化的作用昏兆,但是一旦沒用好就會(huì)造成線程不安全枫虏,本文就來講講如何保證線程安全 ...
    M_慕宸閱讀 529評(píng)論 0 5
  • 前言 多線程開發(fā)是性能優(yōu)化常用的技術(shù),在多線程開發(fā)中爬虱,線程安全是繞不開的一個(gè)話題隶债。線程安全的定義,在之前的文章中也...
    虎啦吧唧的猴閱讀 1,074評(píng)論 0 2
  • 本文主要介紹常見的鎖跑筝,以及synchronized死讹、NSLock、遞歸鎖曲梗、條件鎖的底層分析 鎖 先看一張大家都非常...
    北京_小海閱讀 511評(píng)論 0 0