剖析@synchronizd底層實(shí)現(xiàn)原理

synchronizd是Objective-C中的一個(gè)語(yǔ)法糖,用于給某個(gè)對(duì)象加鎖,因?yàn)槭褂闷饋?lái)簡(jiǎn)單方便调鲸,所以使用頻率很高棘幸。然而焰扳,濫用@synchronizd很容易導(dǎo)致代碼效率低下。本篇博客旨在結(jié)合@synchronizd底層實(shí)現(xiàn)源碼并剖析其實(shí)現(xiàn)原理误续,這樣可以更好的讓我們?cè)谶m合的情景使用@synchronizd吨悍。

@synchronizd本質(zhì)上是一個(gè)編譯器標(biāo)識(shí)符,在Objective-C層面看不其任何信息蹋嵌。因此可以通過(guò)clang -rewrite-objc指令來(lái)獲得@synchronizd的C++實(shí)現(xiàn)代碼畜份。示例代碼如下:

int main(int argc, const char * argv[]) {
    NSString *obj = @"Iceberg";
    @synchronized(obj) {
        NSLog(@"Hello,world! => %@" , obj);
    }
}
int main(int argc, const char * argv[]) {
    
    NSString *obj = (NSString *)&__NSConstantStringImpl__var_folders_8l_rsj0hqpj42b9jsw81mc3xv_40000gn_T_block_main_54f70c_mi_0;
    
    {
        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);
                    }
                    id sync_exit;
                } _sync_exit(_sync_obj);

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_8l_rsj0hqpj42b9jsw81mc3xv_40000gn_T_block_main_54f70c_mi_1 , obj);
                
            } 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);
        }
    }

}

通過(guò)分析C++代碼可以看到@sychronized的實(shí)現(xiàn)主要依賴于兩個(gè)函數(shù):objc_sync_enter和objc_sync_exit。此外還有try{}catch{}語(yǔ)句用于捕捉@sychronized{}語(yǔ)法塊中代碼執(zhí)行過(guò)程中出現(xiàn)的異常欣尼。

我們發(fā)現(xiàn)objc_sync_enter函數(shù)是在try語(yǔ)句之前調(diào)用爆雹,參數(shù)為需要加鎖的對(duì)象。因?yàn)镃++中沒(méi)有try{}catch{}finally{}語(yǔ)句愕鼓,所以不能在finally{}調(diào)用objc_sync_exit函數(shù)钙态。因此objc_sync_exit是在_SYNC_EXIT結(jié)構(gòu)體中的析構(gòu)函數(shù)中調(diào)用,參數(shù)同樣是當(dāng)前加鎖的對(duì)象菇晃。這個(gè)設(shè)計(jì)很巧妙册倒,原因在_SYNC_EXIT結(jié)構(gòu)體類型的_sync_exit是一個(gè)局部變量,生命周期為try{}語(yǔ)句塊磺送,其中包含了@sychronized{}代碼需要執(zhí)行的代碼驻子,在代碼完成后灿意,_sync_exit局部變量出棧釋放,隨即調(diào)用其析構(gòu)函數(shù)崇呵,進(jìn)而調(diào)用objc_sync_exit函數(shù)缤剧。即使try{}語(yǔ)句塊中的代碼執(zhí)行過(guò)程中出現(xiàn)異常,跳轉(zhuǎn)到catch{}語(yǔ)句域慷,局部變量_sync_exit同樣會(huì)被釋放荒辕,完美的模擬了finally的功能。

接下來(lái)犹褒,在蘋果公開(kāi)的源代碼文件objc-sync.mm中找到objc_sync_enter和objc_sync_exit這兩個(gè)函數(shù)的實(shí)現(xiàn)抵窒,一窺其中的奧秘。

typedef struct SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object; //當(dāng)前加鎖的對(duì)象
    int32_t threadCount;  //使用對(duì)object加鎖的線程個(gè)數(shù)
    recursive_mutex_t mutex; //遞歸互斥鎖
} SyncData;

typedef struct {
    SyncData *data;
    unsigned int lockCount;  //表示當(dāng)前線程對(duì)object對(duì)象加鎖次數(shù)
} SyncCacheItem;

typedef struct SyncCache {
    unsigned int allocated;
    unsigned int used;
    SyncCacheItem list[0];
} SyncCache;

/*
  Fast cache: two fixed pthread keys store a single SyncCacheItem. 
  This avoids malloc of the SyncCache for threads that only synchronize 
  a single object at a time.
  SYNC_DATA_DIRECT_KEY  == SyncCacheItem.data
  SYNC_COUNT_DIRECT_KEY == SyncCacheItem.lockCount
 */

struct SyncList {
    SyncData *data;
    spinlock_t lock;

    SyncList() : data(nil) { }
};

// Use multiple parallel lists to decrease contention among unrelated objects.
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;

上述代碼是一些相關(guān)的數(shù)據(jù)結(jié)構(gòu)叠骑,下面分別進(jìn)行介紹:

SyncData結(jié)構(gòu)體中有四個(gè)成員變量李皇,其中object指針變量指向當(dāng)前加鎖對(duì)象,threadCount表示對(duì)object加鎖的線程個(gè)數(shù)宙枷,mutex是一個(gè)遞歸互斥鎖掉房,意味著可以對(duì)object進(jìn)行多次加鎖,其具體作用后面會(huì)提到朦拖。

SyncCacheItem結(jié)構(gòu)體中有兩個(gè)成員變量圃阳,其中data是SyncData結(jié)構(gòu)體類型的指針,lockCount表示當(dāng)前線程對(duì)當(dāng)前結(jié)構(gòu)體對(duì)象加鎖次數(shù)璧帝,其實(shí)就是對(duì)加鎖對(duì)象object的加鎖次數(shù)捍岳。我們可以看到SyncCacheItem與SyncData是一對(duì)一關(guān)系,SyncCacheItem只是對(duì)SyncData進(jìn)行了再次封裝以便于緩存睬隶,具體使用見(jiàn)后文锣夹。

SyncCache結(jié)構(gòu)體中有三個(gè)成員變量,其中維護(hù)了一個(gè)SyncCacheItem類型的數(shù)組苏潜,allocated和used則分別表示當(dāng)前分配的SyncCacheItem數(shù)組中的總個(gè)數(shù)和已經(jīng)使用的個(gè)數(shù)银萍。這個(gè)結(jié)構(gòu)體與線程是一對(duì)一的關(guān)系,用于存儲(chǔ)當(dāng)前線程已加鎖對(duì)象對(duì)應(yīng)的SyncCacheItem結(jié)構(gòu)體恤左,因?yàn)橐粋€(gè)線程可以對(duì)同一個(gè)對(duì)象多次加鎖贴唇,所以通過(guò)引入緩存SyncCache可以提高效率,具體使用見(jiàn)后文飞袋。

SyncList結(jié)構(gòu)體中有兩個(gè)成員變量和一個(gè)構(gòu)造函數(shù)戳气,其中data是SyncData結(jié)構(gòu)體類型的指針,lock是一個(gè)自旋鎖巧鸭。

sDataLists是一個(gè)全局StripedMap哈希列表瓶您,其中value為SyncList對(duì)象,key為加鎖對(duì)象object指針進(jìn)行hash后的值。StripedMap是一個(gè)C++模板類呀袱,其實(shí)現(xiàn)代碼如下所示:

template<typename T>
class StripedMap {

    enum { CacheLineSize = 64 };

#if TARGET_OS_EMBEDDED
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif

    struct PaddedT {
        T value alignas(CacheLineSize);
    };

    PaddedT array[StripeCount];

    static unsigned int indexForPointer(const void *p) {
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
    }

 public:
    T& operator[] (const void *p) { 
        return array[indexForPointer(p)].value; 
    }
    const T& operator[] (const void *p) const { 
        return const_cast<StripedMap<T>>(this)[p]; 
    }

#if DEBUG
    StripedMap() {
        // Verify alignment expectations.
        uintptr_t base = (uintptr_t)&array[0].value;
        uintptr_t delta = (uintptr_t)&array[1].value - base;
        assert(delta % CacheLineSize == 0);
        assert(base % CacheLineSize == 0);
    }
#endif
};

上述代碼中贸毕,由于自己對(duì)C++模板類不熟悉,所以只能看個(gè)大概夜赵。其中有兩個(gè)值得注意的地方明棍,其中StripeCount表示哈希數(shù)組的長(zhǎng)度,如果是嵌入式系統(tǒng)值為8油吭,否則值為64击蹲,也就意味著哈希數(shù)組最大長(zhǎng)度為64署拟;另外indexForPointer函數(shù)是用于計(jì)數(shù)哈希下標(biāo)的函數(shù)婉宰,算法不難,但是很巧妙推穷,值得學(xué)習(xí)心包。

下面開(kāi)始分析相關(guān)的函數(shù)實(shí)現(xiàn),首先找到@sychronized直接調(diào)用的兩個(gè)函數(shù):objc_sync_enter和objc_sync_exit馒铃,代碼如下:

// Begin synchronizing on 'obj'. 
// Allocates recursive mutex associated with 'obj' if needed.
// Returns OBJC_SYNC_SUCCESS once lock is acquired.  
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;
}

// End synchronizing on 'obj'. 
// Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
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;
}

不難發(fā)現(xiàn)蟹腾,上述代碼都調(diào)用了id2data函數(shù)來(lái)獲取一個(gè)與obj對(duì)應(yīng)的SyncData對(duì)象,然后使用該對(duì)象中的遞歸互斥鎖分別進(jìn)行加鎖與解鎖区宇。至此@sychronized的大致實(shí)現(xiàn)過(guò)程已經(jīng)很清晰了娃殖,本質(zhì)上是為一個(gè)對(duì)象分配一把遞歸互斥鎖,可以也是為什么可以反復(fù)使用@sychronized對(duì)同一個(gè)對(duì)象進(jìn)行加鎖的原因议谷。那么@sychronized是如果管理這把互斥鎖炉爆,以及是如何處理多個(gè)線程對(duì)同一個(gè)對(duì)象進(jìn)行多次加鎖的情況?很明顯卧晓,一切奧秘都藏在id2data函數(shù)中芬首,其代碼如下所示:

  • 注:為了描述方便,下面將id2data函數(shù)的形參object描述為同步對(duì)象obejct逼裆。
static SyncData* id2data(id object, enum usage why)
{
    //從全局哈希表sDataLists中獲取object對(duì)應(yīng)的SyncList對(duì)象
    //lockp指針指向SyncList對(duì)象中自旋鎖
    //listp指向一條SyncData鏈表
    spinlock_t *lockp = &LOCK_FOR_OBJ(object);
    SyncData **listp = &LIST_FOR_OBJ(object);
    SyncData* result = NULL;

    //對(duì)于同一個(gè)線程來(lái)說(shuō)郁稍,有兩種緩存方式:
    //第一種:快速緩存(fastCache),適用于一個(gè)線程一次只對(duì)一個(gè)對(duì)象加鎖的情況胜宇,用宏SUPPORT_DIRECT_THREAD_KEYS來(lái)標(biāo)識(shí)
    //這種情況意味著同一時(shí)間內(nèi)耀怜,線程緩存中只有一個(gè)SyncCacheItem對(duì)象,鍵值SYNC_DATA_DIRECT_KEY和SYNC_COUNT_DIRECT_KEY分別對(duì)應(yīng)SyncCacheItem結(jié)構(gòu)體中的SyncData對(duì)象和lockCount.
#if SUPPORT_DIRECT_THREAD_KEYS
    // Check per-thread single-entry fast cache for matching object
    //用于標(biāo)識(shí)當(dāng)前線程的是否已使用fastCache
    bool fastCacheOccupied = NO;
    //直接調(diào)用tls_get_direct函數(shù)獲取SyncData對(duì)象
    SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
    if (data) {
         //標(biāo)識(shí)fastCache已被使用
        fastCacheOccupied = YES;
         //比較fastCache中的SyncData對(duì)象中的object與當(dāng)前同步對(duì)象object是否為同一個(gè)對(duì)象
        if (data->object == object) {
            // Found a match in fast cache.
              //fastCache中的對(duì)象恰好是當(dāng)前同步對(duì)象object桐愉,則后續(xù)處理直接使用fastCache中SyncData對(duì)象
            uintptr_t lockCount;

            result = data;
            //獲取當(dāng)前線程對(duì)應(yīng)當(dāng)前SyncData對(duì)象已經(jīng)加鎖的次數(shù)
            lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
            //無(wú)效的SyncData對(duì)象
            if (result->threadCount <= 0  ||  lockCount <= 0) {
                _objc_fatal("id2data fastcache is buggy");
            }
              //判斷當(dāng)前操作的加鎖還是解鎖
            switch(why) {
            //加鎖
            case ACQUIRE: {
                //加鎖一次
                lockCount++;
                //更新已加鎖次數(shù)
                tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
                break;
            }
            //解鎖
            case RELEASE:
                //解鎖一次
                lockCount--;
                //更新已加鎖次數(shù)
                tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
                //已加鎖次數(shù)為0财破,表示當(dāng)前線程對(duì)當(dāng)前同步對(duì)象object達(dá)到鎖平衡,因此不需要再持有當(dāng)前同步對(duì)象仅财。
                if (lockCount == 0) {
                    // remove from fast cache
                    //將對(duì)應(yīng)的SyncData對(duì)象從線程緩存中移除
                    tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
                    // atomic because may collide with concurrent ACQUIRE
                    //此函數(shù)為原子操作函數(shù)狈究,用于對(duì)32位的threadCount整形變量執(zhí)行減一操作,且確保線程安全。因?yàn)榭赡艽嬖谕粫r(shí)間多個(gè)線程對(duì)一個(gè)threadCount進(jìn)行加減操作抖锥,避免出現(xiàn)多線程競(jìng)爭(zhēng)亿眠。不同于lockCount,threadCount是多個(gè)線程共享的一個(gè)變量磅废,用于記錄對(duì)一個(gè)對(duì)象加鎖的線程個(gè)數(shù)纳像,threadCount對(duì)應(yīng)的SyncData對(duì)象除了線程緩存中持有之外,還存在于全局哈希表sDataLists中拯勉,sDataLists哈希表是多個(gè)線程共享的數(shù)據(jù)結(jié)構(gòu)竟趾,因此存在多線程訪問(wèn)的可能。而lockCount則與線程一一對(duì)應(yīng)且存儲(chǔ)在線程的緩存區(qū)中宫峦,不存在多線性讀寫問(wèn)題岔帽,因此不需要加鎖。
                    OSAtomicDecrement32Barrier(&result->threadCount);
                }
                break;
            case CHECK:
                // do nothing
                break;
            }

            return result;
        }
    }
#endif

    // Check per-thread cache of already-owned locks for matching object
    //這是第二章緩存方式:使用SyncCache結(jié)構(gòu)體來(lái)維護(hù)一個(gè)SyncCacheItem數(shù)組导绷,這樣一個(gè)線程就可以處理對(duì)多個(gè)同步對(duì)象犀勒。值得注意的是SyncCache與線程也是一對(duì)一的關(guān)系。
    //獲取當(dāng)前線程緩存區(qū)中的SyncCache對(duì)象
    SyncCache *cache = fetch_cache(NO);
    if (cache) {
        unsigned int i;
        //遍歷SyncCache對(duì)象中的SyncCacheItem數(shù)組妥曲,匹配當(dāng)前同步對(duì)象object
        for (i = 0; i < cache->used; i++) {
            SyncCacheItem *item = &cache->list[i];
            if (item->data->object != object) continue;

            // Found a match.
            //當(dāng)前同步對(duì)象object已存在的SyncCache中
            //獲取對(duì)應(yīng)的SyncData對(duì)象
            result = item->data;
            //無(wú)效的SyncData對(duì)象
            if (result->threadCount <= 0  ||  item->lockCount <= 0) {
                _objc_fatal("id2data cache is buggy");
            }
            //后續(xù)操作同fastCache一樣贾费,參考fastCache的注釋
            switch(why) {
            case ACQUIRE:
                item->lockCount++;
                break;
            case RELEASE:
                item->lockCount--;
                if (item->lockCount == 0) {
                    // remove from per-thread cache
                    cache->list[i] = cache->list[--cache->used];
                    // atomic because may collide with concurrent ACQUIRE
                    OSAtomicDecrement32Barrier(&result->threadCount);
                }
                break;
            case CHECK:
                // do nothing
                break;
            }
              
            return result;
        }
    }

    // Thread cache didn't find anything.
    // Walk in-use list looking for matching object
    // Spinlock prevents multiple threads from creating multiple 
    // locks for the same new object.
    // We could keep the nodes in some hash table if we find that there are
    // more than 20 or so distinct locks active, but we don't do that now.
    
    //如果當(dāng)前線程中的緩存中沒(méi)有找到當(dāng)前同步對(duì)象對(duì)應(yīng)的SyncData對(duì)象,則在全局哈希表中查找
    //因?yàn)槿止1硎嵌鄠€(gè)線程共享的數(shù)據(jù)結(jié)構(gòu)檐盟,因此需要進(jìn)行加鎖處理
    lockp->lock();

    {
        SyncData* p;
        SyncData* firstUnused = NULL;
        //遍歷當(dāng)前同步對(duì)象obejct在全局哈希表中的SyncData鏈表褂萧。這里之所以使用鏈表,是因?yàn)楣1淼膆ash算法不能確保hash的唯一性葵萎,存在多個(gè)對(duì)象對(duì)應(yīng)一個(gè)hash值的情況导犹。
        for (p = *listp; p != NULL; p = p->nextData) {
              //哈希表中存在對(duì)應(yīng)的SyncData對(duì)象
            if ( p->object == object ) {
                result = p;
                // atomic because may collide with concurrent RELEASE
                //此函數(shù)為原子操作函數(shù),確保線程安全陌宿,用于對(duì)32位的threadCount整形變量執(zhí)行加一操作锡足,表示占用當(dāng)前同步對(duì)象的線程數(shù)加1。
                OSAtomicIncrement32Barrier(&result->threadCount);
                goto done;
            }
            //用于標(biāo)記一個(gè)空閑的SyncData對(duì)象
            if ( (firstUnused == NULL) && (p->threadCount == 0) )
                firstUnused = p;
        }
    
        // no SyncData currently associated with object
        //由于此時(shí)同步對(duì)象object沒(méi)有對(duì)應(yīng)的SyncData對(duì)象壳坪,因此RELEASE與CHECK都屬于無(wú)效操作
        if ( (why == RELEASE) || (why == CHECK) )
            goto done;
    
        // an unused one was found, use it
        //如果沒(méi)有找到匹配的SyncData對(duì)象且存在空閑的SyncData對(duì)象舶得,則直接使用,不需要?jiǎng)?chuàng)建新的SyncData爽蝴,以提高效率沐批。
        if ( firstUnused != NULL ) {
            result = firstUnused;
            //關(guān)聯(lián)當(dāng)前同步對(duì)象
            result->object = (objc_object *)object;
            //重置占用線程為1
            result->threadCount = 1;
            goto done;
        }
    }

    // malloc a new SyncData and add to list.
    // XXX calling malloc with a global lock held is bad practice,
    // might be worth releasing the lock, mallocing, and searching again.
    // But since we never free these guys we won't be stuck in malloc very often.
    
    //到這一步說(shuō)明需要新建一個(gè)SyncData對(duì)象
    result = (SyncData*)calloc(sizeof(SyncData), 1);
    result->object = (objc_object *)object;
    result->threadCount = 1;
    //創(chuàng)建遞歸互斥鎖
    new (&result->mutex) recursive_mutex_t();
    //以“入棧”的方式加入當(dāng)前同步對(duì)象object對(duì)應(yīng)的SyncData鏈表
    result->nextData = *listp;
    *listp = result;
    
 done:
     //對(duì)全局哈希表的操作結(jié)束蝎亚,解鎖
    lockp->unlock();
    if (result) {
        // Only new ACQUIRE should get here.
        // All RELEASE and CHECK and recursive ACQUIRE are 
        // handled by the per-thread caches above.
        //只有ACQUIRE才需要新建SyncData對(duì)象
        if (why == RELEASE) {
            // Probably some thread is incorrectly exiting 
            // while the object is held by another thread.
            return nil;
        }
        if (why != ACQUIRE) _objc_fatal("id2data is buggy");
        if (result->object != object) _objc_fatal("id2data is buggy");

         //fastCache緩存模式
#if SUPPORT_DIRECT_THREAD_KEYS
        if (!fastCacheOccupied) {
            // Save in fast thread cache
            //直接緩存新建的SyncData對(duì)象
            tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
            //設(shè)置加鎖次數(shù)為1
            tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
        } else 
#endif
         //SyncCache緩存模式九孩,則直接加入SyncCacheItem數(shù)組中
        {
            // Save in thread cache
            if (!cache) cache = fetch_cache(YES);
            cache->list[cache->used].data = result;
            cache->list[cache->used].lockCount = 1;
            cache->used++;
        }
    }

    return result;
}

通過(guò)上述代碼的注釋,id2data函數(shù)的功能已經(jīng)大致清晰发框。id2data函數(shù)主要是用于管理同步對(duì)象object與線程之間的關(guān)聯(lián)躺彬。不論是ACQUIRE、RELEASE還是CHECK操作,都會(huì)先從當(dāng)前線程的緩存中去獲取對(duì)應(yīng)的SyncData對(duì)象宪拥。如果當(dāng)前線程的緩存區(qū)中不存在仿野,那么再?gòu)娜值墓?shù)組中查找,查看其它線程是否已經(jīng)占用過(guò)當(dāng)前同步對(duì)象object她君。如果還是沒(méi)有脚作,那么就新建一個(gè)與之對(duì)應(yīng)的SyncData對(duì)象,分別加入全局哈希表和當(dāng)前線程緩存中缔刹。

至此球涛,@synchronized的實(shí)現(xiàn)原理已經(jīng)剖析結(jié)束,其有一個(gè)最大的特點(diǎn)是:不論是多個(gè)線性同一時(shí)間內(nèi)對(duì)一個(gè)對(duì)象進(jìn)行多次同步還是一個(gè)線程對(duì)同一個(gè)對(duì)象同步多次校镐,一個(gè)對(duì)象只分配一把遞歸互斥鎖亿扁。也就意味著對(duì)同一個(gè)對(duì)象而言,當(dāng)執(zhí)行某一次同步操作時(shí)灭翔,其他線程或同一線程的其他同步操作都會(huì)被阻塞魏烫,不言而喻辣苏,這種加鎖方式的效率是很低的肝箱。

下面代碼展示了@synchronized經(jīng)典的低效率使用案例之一:

- (void)setInstanceMemberObjecObject1:(id)value {
    @synchronized(self) {
        self.instanceMember1 = value;
    }
}

- (void)setInstanceMemberObjecObject2:(id)value {
    @synchronized(self) {
        self.instanceMember2 = value;
    }
}

- (void)setInstanceMemberObjecObject3:(id)value {
    @synchronized(self) {
        self.instanceMember3 = value;
    }
}

上述代碼,調(diào)用其中一個(gè)設(shè)置函數(shù)時(shí)稀蟋,另外兩個(gè)成員變量的設(shè)置函數(shù)在同一時(shí)間被調(diào)用都會(huì)被阻塞煌张。這里@synchronized同步的代碼很簡(jiǎn)單,所以不會(huì)效率差別不大退客。如果是同步的代碼需要執(zhí)行較長(zhǎng)的時(shí)間骏融,且被多個(gè)線程并發(fā)調(diào)用,那么效率變得很低萌狂。如果不清楚@synchronized的實(shí)現(xiàn)原理档玻,可能很難排查出來(lái)導(dǎo)致效率低下的問(wèn)題所在。我建議使用GCD取代@synchronized實(shí)現(xiàn)同步功能茫藏,GCD不僅是線程安全误趴,且其由底層實(shí)現(xiàn),效率會(huì)好很多务傲。我們發(fā)生@synchronized的底層實(shí)現(xiàn)有捕獲異常的功能凉当,因此適合在需要確保發(fā)生錯(cuò)誤時(shí)代碼不會(huì)死鎖,而是拋出異常時(shí)使用售葡。
博客地址

最后編輯于
?著作權(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)離奇詭異贮缅,居然都是意外死亡瓜贾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門携悯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)祭芦,“玉大人,你說(shuō)我怎么就攤上這事憔鬼」昃ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵轴或,是天一觀的道長(zhǎng)昌跌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)照雁,這世上最難降的妖魔是什么蚕愤? 我笑而不...
    開(kāi)封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮饺蚊,結(jié)果婚禮上萍诱,老公的妹妹穿的比我還像新娘。我一直安慰自己污呼,他們只是感情好裕坊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著燕酷,像睡著了一般籍凝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苗缩,一...
    開(kāi)封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天饵蒂,我揣著相機(jī)與錄音,去河邊找鬼酱讶。 笑死退盯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浴麻。 我是一名探鬼主播得问,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼软免!你這毒婦竟也來(lái)了宫纬?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤膏萧,失蹤者是張志新(化名)和其女友劉穎漓骚,沒(méi)想到半個(gè)月后蝌衔,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炭庙。 院中可真熱鬧饲窿,春花似錦、人聲如沸煤搜。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)擦盾。三九已至,卻和暖如春淌哟,著一層夾襖步出監(jiān)牢的瞬間迹卢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工徒仓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腐碱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓掉弛,卻偏偏與公主長(zhǎng)得像症见,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子殃饿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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