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í)使用售葡。
博客地址