1.@synchronized原理
synchronized中傳入的object的內存地址,被用作key,通過hash map對應的一個系統(tǒng)維護的遞歸鎖。所以不管是傳入什么類型的object,只要是有內存地址,就能啟動同步代碼塊的效果。
2.注意事項
- synchronized是使用的遞歸mutex來做同步。
NSObject *obj = [[NSObject alloc]init];
@synchronized (obj) {
NSLog(@"1st sync");
@synchronized (obj) {
NSLog(@"2nd sync");
}
}
執(zhí)行結果:
2019-07-15 17:59:27.398820+0800 xxtest[63251:2852986] 1st sync
2019-07-15 17:59:27.399013+0800 xxtest[63251:2852986] 2nd sync
-
@synchronized(nil)不起任何作用
@synchronized(nil)不起任何作用,表明我們需要適當關注傳入的object的聲明周期,一旦置為nil之后就無法做代碼同步了。 -
慎用@synchronized(self) (盡量不要這樣使用)
不要使用@synchronized(self)。
不少代碼都是直接將self傳入@synchronized當,容易導致死鎖的出現(xiàn)。
//class A
@synchronized (self) {
[_sharedLock lock];
NSLog(@"code in class A");
[_sharedLock unlock];
}
//class B
[_sharedLock lock];
@synchronized (objectA) {
NSLog(@"code in class B");
}
[_sharedLock unlock];
原因是self很可能會被外部對象訪問,被用作key來生成一鎖,類似上述代碼中的@synchronized (objectA)。兩個公共鎖交替使用的場景就容易出現(xiàn)死鎖。
正確的做法是傳入一個類內部維護的NSObject對象,而且這個對象是對外不可見的。
- 注意粒度控制
@synchronized (sharedToken) {
[arrA addObject:obj];
}
@synchronized (sharedToken) {
[arrB addObject:obj];
}
使用同一個token來同步arrA和arrB的訪問,雖然arrA和arrB之間沒有任何聯(lián)系。
應該是不同的數(shù)據(jù)使用不同的鎖,盡量將粒度控制在最細的程度
應該優(yōu)化成如下代碼:
@synchronized (tokenA) {
[arrA addObject:obj];
}
@synchronized (tokenB) {
[arrB addObject:obj];
}
-
注意內部的函數(shù)調用
@synchronized還有個很容易變慢的場景,就是{}內部有其他隱蔽的函數(shù)調用。比如:
@synchronized (tokenA) {
[arrA addObject:obj];
[self doSomethingWithA:arrA];
}
doSomethingWithA內部可能又調用了其他函數(shù),維護doSomethingWithA的工程師可能并沒有意識到自己是被鎖同步的,由此層層疊疊可能引入更多的函數(shù)調用,代碼就莫名其妙的越來越慢了,感覺鎖的性能差,其實是我們沒用好。