iOS中的多種鎖(Lock)

在查看SDWebImage這種第三方的時(shí)候會(huì)發(fā)現(xiàn)有些地方有用到鎖,其中最常見(jiàn)的就是@ synchronized滓鸠,所以今天我們就由點(diǎn)到面的來(lái)了解一下iOS的各種鎖。下面我們用腦圖來(lái)分析下鎖的各種類(lèi)型:
各種鎖.png

問(wèn)題場(chǎng)景:

開(kāi)發(fā)數(shù)中會(huì)難免會(huì)遇到多線程競(jìng)爭(zhēng)資源的問(wèn)題婚度,從而帶來(lái)了線程安全的問(wèn)題伟葫。
所謂線程安全:當(dāng)一個(gè)線程訪問(wèn)數(shù)據(jù)的時(shí)候,其他線程不能對(duì)其訪問(wèn)部逮,直至該線程訪問(wèn)完畢娜汁。簡(jiǎn)單來(lái)講就是同一時(shí)刻對(duì)同一個(gè)數(shù)據(jù)操作的線程只有一個(gè)。只有確保了這樣甥啄,才能使數(shù)據(jù)不會(huì)被其他線程影響存炮。
那么,我們?cè)鯓颖WC線程安全呢蜈漓?
此時(shí),鎖就派上用場(chǎng)了宫盔,可以確保同一時(shí)刻只有同一個(gè)線程對(duì)同一個(gè)數(shù)據(jù)源進(jìn)行訪問(wèn)融虽。

1、@synchronized鎖

@synchronized(美 [,s?nkr?na?'ze??n]:同步)是OC層面的鎖灼芭,synchronized block 與 [_lock lock] & [_lock unlock] 效果相同有额,但語(yǔ)法更加簡(jiǎn)潔可讀,但代價(jià)是性能的降低彼绷。
官網(wǎng)介紹:防止不同的線程同時(shí)獲取相同的鎖巍佑。
知識(shí)網(wǎng)址:http://yulingtianxia.com/blog/2015/11/01/More-than-you-want-to-know-about-synchronized/
重點(diǎn):@synchronized 結(jié)構(gòu)在工作時(shí)為傳入的對(duì)象分配了一個(gè)遞歸鎖。所謂遞歸鎖是在被同一個(gè)線程重復(fù)調(diào)用時(shí)不會(huì)產(chǎn)生死鎖寄悯。NSRecursiveLock(遞歸鎖)類(lèi)也是這樣的萤衰,我們后面會(huì)有分析。
特殊情況:
1猜旬、你調(diào)用 sychronized 的每個(gè)對(duì)象脆栋,Objective-C runtime 都會(huì)為其分配一個(gè)遞歸鎖并存儲(chǔ)在哈希表中。
2洒擦、如果在 sychronized 內(nèi)部對(duì)象被釋放或被設(shè)為 nil 看起來(lái)都 OK椿争。不過(guò)這沒(méi)在文檔中說(shuō)明,所以我不會(huì)再生產(chǎn)代碼中依賴這條熟嫩。
3秦踪、注意不要向你的 sychronized block 傳入 nil!這將會(huì)從代碼中移走線程安全。你可以通過(guò)在 objc_sync_nil 上加斷點(diǎn)來(lái)查看是否發(fā)生了這樣的事情椅邓。

代碼如下:
- (void)synchronizedLock{
    /*
     NSMutableArray *_elements;
     _elements在任何情況下都只會(huì)在一個(gè)線程中運(yùn)行
     */
    @synchronized(_elements){
        [_elements addObject:@"1"];
    };
}

2舍扰、NSLock鎖

原理:NSLock實(shí)現(xiàn)了最基本的互斥鎖,遵循NSLocking協(xié)議希坚,通過(guò)lock和unLock來(lái)進(jìn)行鎖定于解鎖边苹。當(dāng)一個(gè)線程訪問(wèn)的時(shí)候,該線程獲得鎖裁僧,其他線程訪問(wèn)的時(shí)候个束,將被操作系統(tǒng)掛起,直到該線程釋放鎖聊疲,其他線程才能對(duì)其進(jìn)行訪問(wèn)茬底,從而確保線程安全。如果連續(xù)鎖定获洲,則會(huì)造成死鎖問(wèn)題阱表。

代碼如下:
/*
 A :lock的最簡(jiǎn)單使用
 */
- (void)initLock{
    //1、對(duì)鎖進(jìn)行初始化
    _elements = [NSMutableArray array];
    _lock = [[NSLock alloc] init];
}

- (void)push:(id)element{
    //2贡珊、上鎖
    [_lock lock];
    [_elements addObject:element];
    //3最爬、解鎖
    [_lock unlock];
    
}
/*
 B :lock的結(jié)合GCD多線程調(diào)用使用
 */
- (void)GDCAndLock{
    _lock = [[NSLock alloc]init];
    
    //在多個(gè)線程中調(diào)用。由于使用鎖的線程鎖是沒(méi)有執(zhí)行完畢的门岔,所以其他顯線程不能調(diào)用爱致,直到執(zhí)行完畢后,才允許其他線程調(diào)用寒随。
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1");
        [self lockFounction:[NSThread currentThread] num: 1];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2");
        [self lockFounction:[NSThread currentThread] num: 2];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3");
        [self lockFounction:[NSThread currentThread] num: 3];
    });
    
  
}
- (void)lockFounction:(NSThread *)thread num:(NSInteger) num {
    [_lock lock];
    NSLog(@"thread - %@, num - %ld", thread, num);
    sleep(5);
    [_lock unlock];
}

打印如圖:


打印.png
/*
 C :lock的tryLock和lockBeforeDate兩個(gè)方法的使用糠悯。
 tryLock方法會(huì)嘗試加鎖,如果鎖不可用(已經(jīng)被鎖住)妻往,則并不會(huì)阻塞線程互艾,并返回NO。lockBeforeDate:方法會(huì)在所指定Date之前嘗試加鎖讯泣,如果在指定時(shí)間之前都不能加鎖纫普,則返回NO。
 */

- (void)tryLockAndDate{
    _lock = [[NSLock alloc]init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //lockBeforeDate會(huì)在指定的時(shí)間之前加鎖判帮,所以已經(jīng)使用過(guò)[_lock lock]了.下面相當(dāng)于在當(dāng)前時(shí)間之前上鎖了局嘁。
        [_lock lockBeforeDate:[NSDate date]];
        NSLog(@"1需要線程同步的操作1 開(kāi)始");
        sleep(2);
        NSLog(@"1需要線程同步的操作1 結(jié)束");
        [_lock unlock];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        if ([_lock tryLock]) {//嘗試獲取鎖,如果獲取不到返回NO晦墙,不會(huì)阻塞該線程
            NSLog(@"2鎖可用的操作");
            [_lock unlock];
        }else{
            NSLog(@"2鎖不可用的操作");
        }
        NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:3];
        if ([_lock lockBeforeDate:date]) {
            //嘗試在未來(lái)的3s內(nèi)獲取鎖悦昵,并阻塞該線程,如果3s內(nèi)獲取不到恢復(fù)線程, 返回NO,不會(huì)阻塞該線程
            NSLog(@"2沒(méi)有超時(shí)晌畅,獲得鎖");
            [_lock unlock];
        }else{
            NSLog(@"2超時(shí)但指,沒(méi)有獲得鎖");
        }
    });
    
}

3、遞歸鎖NSRecursiveLock

NSRecursiveLock遞歸鎖可以被同一線程多次請(qǐng)求,但不會(huì)引起死鎖棋凳。這主要是用在循環(huán)或者遞歸操作場(chǎng)景中拦坠。

- (void)useNSRecursiveLock{
    //如果使用_lock會(huì)招致死鎖,因?yàn)楸煌粋€(gè)線程多次調(diào)用剩岳。每次進(jìn)入這個(gè)block時(shí)贞滨,都會(huì)去加一次鎖,而從第二次開(kāi)始拍棕,由于鎖已經(jīng)被使用了且沒(méi)有解鎖晓铆,所以它需要等待鎖被解除,這樣就導(dǎo)致了死鎖绰播,線程被阻塞住了骄噪。
//    _lock = [[NSLock alloc] init];
    NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //創(chuàng)建一個(gè)靜態(tài)方法,block方法
        static void (^RecursiveMethod)(int);
        RecursiveMethod = ^(int value) {
            [lock lock];
            if (value > 0) {
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            [lock unlock];
        };
        RecursiveMethod(5);//方法內(nèi)部判斷來(lái)執(zhí)行5次
    });
}

4蠢箩、NSConditionLock條件鎖

當(dāng)我們?cè)谑褂枚嗑€程的時(shí)候链蕊,只有一把會(huì)lock和unlock的鎖就不能滿足我們的需要了。因?yàn)槠胀ǖ逆i只關(guān)心鎖與不鎖谬泌,但是并不在乎什么時(shí)候才能開(kāi)鎖滔韵,而在處理資源共享場(chǎng)景的時(shí)候,多數(shù)情況下只有滿足一定條件下才能打開(kāi)這把鎖呵萨。(Condition:美 [k?n'd???n] 條件)
NSConditionLock實(shí)現(xiàn)步驟:
NSConditionLock實(shí)現(xiàn)了NSLocking協(xié)議奏属,一個(gè)線程會(huì)等待另一個(gè)線程unlock或者unlockWithCondition:之后再走lock或者lockWhenCondition:之后的代碼。
鎖定和解鎖的調(diào)用可以隨意組合潮峦,也就是說(shuō) lock、lockWhenCondition:與unlock勇婴、unlockWithCondition: 是可以按照自己的需求隨意組合的忱嘹。
劃重點(diǎn):
1、只有 condition 參數(shù)與初始化時(shí)候的 condition 相等耕渴,lock 才能正確進(jìn)行加鎖操作拘悦。
2、unlockWithCondition: 并不是當(dāng) condition 符合條件時(shí)才解鎖橱脸,而是解鎖之后础米,修改 condition 的值。

/*
 在線程 1 解鎖成功之后添诉,線程 2 并沒(méi)有加鎖成功屁桑,而是繼續(xù)等了 1 秒之后線程 3 加鎖成功,這是因?yàn)榫€程 2 的加鎖條件不滿足栏赴,初始化時(shí)候的 condition 參數(shù)為 0蘑斧,而線程 2
 加鎖條件是 condition 為 1,所以線程 2 加鎖失敗。
 lockWhenCondition 與 lock 方法類(lèi)似竖瘾,加鎖失敗會(huì)阻塞線程沟突,所以線程 2 會(huì)被阻塞著。
 tryLockWhenCondition: 方法就算條件不滿足捕传,也會(huì)返回 NO惠拭,不會(huì)阻塞當(dāng)前線程。
 lockWhenCondition:beforeDate:方法會(huì)在約定的時(shí)間內(nèi)一直等待 condition 變?yōu)?2庸论,并阻塞當(dāng)前線程职辅,直到超時(shí)后返回 NO。
 */

代碼:

- (void)nsconditionlock {
    NSConditionLock * cjlock = [[NSConditionLock alloc] initWithCondition:0];
    
    //1葡公、線程 1 解鎖成功
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [cjlock lock];
        NSLog(@"線程1加鎖成功");
        sleep(1);//線程休眠一秒
        [cjlock unlock];
        NSLog(@"線程1解鎖成功");
    });
    
    //2罐农、初始化時(shí)候的 condition 參數(shù)為0,所以此處加鎖失敗催什,返回NO涵亏,此處線程阻塞。全部現(xiàn)成執(zhí)行完畢后執(zhí)行此處鎖
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);//線程休眠一秒
        [cjlock lockWhenCondition:1];
        NSLog(@"線程2加鎖成功");
        [cjlock unlock];
        NSLog(@"線程2解鎖成功");
    });
    
    //3蒲凶、tryLockWhenCondition嘗試加鎖  初始化時(shí)候的 condition 參數(shù)為0气筋,所以此處加鎖成功。方法就算條件不滿足旋圆,也會(huì)返回 NO宠默,不會(huì)阻塞當(dāng)前線程。
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);
        
        if ([cjlock tryLockWhenCondition:0]) {
            NSLog(@"線程3加鎖成功");
            sleep(2);
            /*
             A:成功案例
             這里會(huì)先解鎖當(dāng)前的鎖灵巧,之后修改condition的值為100.在下一個(gè)condition為100的線程中會(huì)加解鎖成功搀矫,如果下個(gè)鎖中的condition等待的值不是100,那么就會(huì)導(dǎo)致加鎖失敗刻肄。
             */
            [cjlock unlockWithCondition:100];
            NSLog(@"線程3解鎖成功");
            
            /*
             B:失敗案例
             [cjlock unlockWithCondition:4];
             NSLog(@"線程3仍然會(huì)解鎖成功瓤球,之后修改condition的值為4");
             */
            
        } else {
            NSLog(@"線程3嘗試加鎖失敗");
        }
    });
    
    //4、lockWhenCondition:beforeDate:方法會(huì)在約定的時(shí)間內(nèi)一直等待 condition 變?yōu)?2敏弃,并阻塞當(dāng)前線程卦羡,直到超時(shí)后返回 NO。
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([cjlock lockWhenCondition:100 beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
            NSLog(@"線程100加鎖成功");
            [cjlock unlockWithCondition:1];
            NSLog(@"線程100解鎖成功");
        } else {
            NSLog(@"線程100嘗試加鎖失敗");
        }
    });
}

5麦到、NSCondition

定義及使用:NSCondition 是一種特殊類(lèi)型的鎖绿饵,通過(guò)它可以實(shí)現(xiàn)不同線程的調(diào)度。A線程被某一個(gè)條件所阻塞瓶颠,直到B線程滿足該條件拟赊,從而發(fā)送信號(hào)給A線程使得A線程繼續(xù)執(zhí)行,例如:你可以開(kāi)啟一個(gè)線程下載圖片步清,一個(gè)線程處理圖片要门。這樣的話虏肾,需要處理圖片的線程由于沒(méi)有圖片會(huì)阻塞,當(dāng)下載線程下載完成之后欢搜,則滿足了需要處理圖片的線程的需求封豪,這樣可以給定一個(gè)信號(hào),讓處理圖片的線程恢復(fù)運(yùn)行炒瘟。
重點(diǎn):
1吹埠、NSCondition 的對(duì)象實(shí)際上作為一個(gè)鎖和一個(gè)線程檢查器,鎖上之后疮装,其他線程也能繼續(xù)上鎖缘琅,之后根據(jù)條件決定是否繼續(xù)運(yùn)行線程,如果線程進(jìn)入waiting狀態(tài)廓推,當(dāng)其他線程中的該鎖執(zhí)行signal(信號(hào))或者broadcast(廣播)時(shí)刷袍,線程被喚醒,繼續(xù)運(yùn)行該線程之后的方法樊展。呻纹。
2、NSCondition 可以手動(dòng)控制現(xiàn)成的掛起和喚醒专缠,可以利用這個(gè)特性設(shè)置依賴雷酪。
特別提醒:
signal只是喚醒單個(gè)線程,broadcast喚醒所有的線程涝婉。
broadcast :廣播 { 英 ['br??dkɑ?st] 美 ['br?dk?st]}

- (void)nsCondition {
    NSCondition * cjcondition = [NSCondition new];
    /*
     在加上鎖之后哥力,調(diào)用條件對(duì)象的 wait 或 waitUntilDate: 方法來(lái)阻塞線程,直到條件對(duì)象發(fā)出喚醒信號(hào)或者超時(shí)之后墩弯,再進(jìn)行之后的操作吩跋。
     */
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [cjcondition lock];
        NSLog(@"線程1線程加鎖----NSTreat:%@",[NSThread currentThread]);
        [cjcondition wait];
        NSLog(@"線程1線程喚醒");
        [cjcondition unlock];
        NSLog(@"線程1線程解鎖");
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [cjcondition lock];
        NSLog(@"線程2線程加鎖----NSTreat:%@",[NSThread currentThread]);
        if ([cjcondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
            NSLog(@"線程2線程喚醒");
            [cjcondition unlock];
            NSLog(@"線程2線程解鎖");
        }
    });

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(2);
        /*
1、休眠時(shí)間如果超過(guò)了線程中條件鎖等待的時(shí)間渔工,那么所有的線程都不會(huì)被喚醒钞澳。不管是哪一個(gè)線程中設(shè)置的時(shí)間,都不能超時(shí)涨缚,否則就會(huì)返回NO,全部不執(zhí)行策治!切記切記脓魏!
2、一次只能喚醒一個(gè)線程通惫,要調(diào)用多次才可以喚醒多個(gè)線程茂翔,如下調(diào)用兩次,將休眠的兩個(gè)線程解鎖履腋。
3珊燎、喚醒的順序?yàn)榫€程添加的順序惭嚣。
*/
        
        [cjcondition signal];
        [cjcondition signal];

        //一次性全部喚醒
        //[cjcondition broadcast];
    });
}

6、dispatch_semaphore信號(hào)量

GCD的信號(hào)量機(jī)制實(shí)現(xiàn)鎖悔政,等待信號(hào)和發(fā)送信號(hào)晚吞。
1、dispatch_semaphore 是 GCD 用來(lái)同步的一種方式谋国,與他相關(guān)的只有三個(gè)函數(shù)槽地,一個(gè)是創(chuàng)建信號(hào)量,一個(gè)是等待信號(hào)芦瘾,一個(gè)是發(fā)送信號(hào)捌蚊。
2、dispatch_semaphore的機(jī)制就是當(dāng)有多個(gè)線程進(jìn)行訪問(wèn)的時(shí)候近弟,只要有一個(gè)獲得了信號(hào)缅糟,其他線程就必須等待該信號(hào)的釋放。

 1祷愉、dispatch_semaphore 和 NSCondition 類(lèi)似窗宦,都是一種基于信號(hào)的同步方式,但 NSCondition 信號(hào)只能發(fā)送谣辞,不能保存(如果沒(méi)有線程在等待迫摔,則發(fā)送的信號(hào)會(huì)失效)。而 dispatch_semaphore 能保存發(fā)送的信號(hào)泥从。dispatch_semaphore 的核心是 dispatch_semaphore_t 類(lèi)型的信號(hào)量句占。
 2、dispatch_semaphore_create(1) 方法可以創(chuàng)建一個(gè) dispatch_semaphore_t ( 英  ['sem?f??])類(lèi)型的信號(hào)量躯嫉,設(shè)定信號(hào)量的初始值為 1纱烘。注意,這里的傳入的參數(shù)必須大于或等于 0,否則 dispatch_semaphore_create 會(huì)返回 NULL皆看。
 3遗锣、dispatch_semaphore_wait(semaphore, overTime); 方法會(huì)判斷 semaphore 的信號(hào)值是否大于 0。大于 0 不會(huì)阻塞線程哺壶,消耗掉一個(gè)信號(hào),執(zhí)行后續(xù)任務(wù)蜒谤。如果信號(hào)值為 0山宾,該線程會(huì)和 NSCondition 一樣直接進(jìn)入 waiting狀態(tài),等待其他線程發(fā)送信號(hào)喚醒該線程執(zhí)行后續(xù)任務(wù)鳍徽,或者當(dāng) overTime 時(shí)限到了资锰,也會(huì)執(zhí)行后續(xù)任務(wù)。
 4阶祭、dispatch_semaphore_signal(semaphore); 發(fā)送信號(hào)绷杜,如果沒(méi)有等待的線程接受信號(hào)直秆,則使 signal 信號(hào)值加一(做到對(duì)信號(hào)的保存)。
 5鞭盟、一個(gè) dispatch_semaphore_wait(semaphore, overTime); 方法會(huì)去對(duì)應(yīng)一個(gè) dispatch_semaphore_signal(semaphore); 看起來(lái)像 NSLock 的 lock 和 unlock圾结,其實(shí)可以這樣理解,區(qū)別只在于有信號(hào)量這個(gè)參數(shù)懊缺,疫稿,lock unlock 只能同一時(shí)間,只能有一個(gè)線程訪問(wèn)被保護(hù)的臨界區(qū)鹃两,而如果信號(hào)量參數(shù)初始值為 x遗座,那么就會(huì)有 x 個(gè)線程可以同時(shí)訪問(wèn)被保護(hù)的臨界區(qū)。
 */
- (void)useDispatch_semaphore {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    // overTime設(shè)置為6秒
    dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
   
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, overTime);
        NSLog(@"線程1開(kāi)始");
        sleep(5);
        NSLog(@"線程1結(jié)束");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        dispatch_semaphore_wait(semaphore, overTime);
        NSLog(@"線程2開(kāi)始");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        dispatch_semaphore_wait(semaphore, overTime);
        NSLog(@"線程3開(kāi)始");
        dispatch_semaphore_signal(semaphore);
    });
}

7俊扳、OSSpinLock自旋鎖

首先導(dǎo)入:#import <libkern/OSAtomic.h>
OSSpinLock 是一種自旋鎖途蒋,和互斥鎖類(lèi)似,都是為了保證線程安全的鎖馋记。但是這是不一樣的:
互斥鎖:當(dāng)一個(gè)線程獲得此鎖之后号坡,其他線程再獲取將會(huì)被阻塞,直到該鎖被釋放梯醒。
自旋鎖:當(dāng)一個(gè)線程獲得此鎖之后宽堆,其他線程將會(huì)一直循環(huán)查看該鎖是否被釋放。鎖比較適用于鎖的持有者保存時(shí)間較短的情況下茸习。
OSSpinLock自旋鎖只有加鎖畜隶、嘗試加鎖和解鎖三個(gè)方法。

/*
 YY大神 @ibireme 的文章也有說(shuō)這個(gè)自旋鎖存在優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題号胚,具體文章可以戳 不再安全的 OSSpinLock籽慢,而 OSSpinLock 在iOS 10.0中被 <os/lock.h> 中的 os_unfair_lock 取代。
常用的相關(guān)API:
 // 初始化
 os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);
 // 加鎖
 os_unfair_lock_lock(unfairLock);
 // 嘗試加鎖
 BOOL b = os_unfair_lock_trylock(unfairLock);
 // 解鎖
 os_unfair_lock_unlock(unfairLock);
 os_unfair_lock 用法和 OSSpinLock 基本一致猫胁,就不一一列出了箱亿。
 */
- (void)osspinlock {
    __block OSSpinLock theLock = OS_SPINLOCK_INIT;//在iOS10之后被ns_unfair_lock替換
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        OSSpinLockLock(&theLock);
        NSLog(@"線程1開(kāi)始");
        sleep(3);
        NSLog(@"線程1結(jié)束");
        OSSpinLockUnlock(&theLock);
        
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        OSSpinLockLock(&theLock);
        sleep(1);
        NSLog(@"線程2");
        OSSpinLockUnlock(&theLock);
        
    });
}

8、鎖的總結(jié)

其實(shí)每一種鎖基本上都是加鎖弃秆、等待届惋、解鎖的步驟,理解了這三個(gè)步驟就可以幫你快速的學(xué)會(huì)各種鎖的用法菠赚。
1盼樟、@synchronized 的效率最低,不過(guò)它的確用起來(lái)最方便锈至,所以如果沒(méi)什么性能瓶頸的話,可以選擇使用 @synchronized译秦。
2峡捡、當(dāng)性能要求較高時(shí)候击碗,可以使用 pthread_mutex 或者 dispath_semaphore,由于 OSSpinLock 不能很好的保證線程安全们拙,而在只有在 iOS10 中才有 os_unfair_lock 稍途,所以,前兩個(gè)是比較好的選擇砚婆。既可以保證速度械拍,又可以保證線程安全。
3装盯、對(duì)于 NSLock 及其子類(lèi)坷虑,速度來(lái)說(shuō) NSLock < NSCondition < NSRecursiveLock < NSConditionLock 。

好了埂奈,要趕火車(chē)回家過(guò)年了迄损,來(lái)年開(kāi)春了在接著寫(xiě)啦。大家新春快樂(lè)账磺!?

參考資料:
http://www.jb51.net/article/127573.htm
http://blog.csdn.net/liupinghui/article/details/67637830
最后奉上本人總結(jié)的代碼:https://github.com/caiqingchong/iOSLock

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芹敌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子垮抗,更是在濱河造成了極大的恐慌氏捞,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冒版,死亡現(xiàn)場(chǎng)離奇詭異液茎,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)壤玫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)豁护,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人欲间,你說(shuō)我怎么就攤上這事楚里。” “怎么了猎贴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵班缎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我她渴,道長(zhǎng)达址,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任趁耗,我火速辦了婚禮沉唠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苛败。我一直安慰自己满葛,他們只是感情好径簿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著嘀韧,像睡著了一般篇亭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锄贷,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天译蒂,我揣著相機(jī)與錄音,去河邊找鬼谊却。 笑死柔昼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的因惭。 我是一名探鬼主播岳锁,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蹦魔!你這毒婦竟也來(lái)了激率?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勿决,失蹤者是張志新(化名)和其女友劉穎乒躺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體低缩,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘉冒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咆繁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讳推。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖玩般,靈堂內(nèi)的尸體忽然破棺而出银觅,到底是詐尸還是另有隱情,我是刑警寧澤坏为,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布究驴,位于F島的核電站,受9級(jí)特大地震影響匀伏,放射性物質(zhì)發(fā)生泄漏洒忧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一够颠、第九天 我趴在偏房一處隱蔽的房頂上張望熙侍。 院中可真熱鬧,春花似錦、人聲如沸核行。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芝雪。三九已至,卻和暖如春综苔,著一層夾襖步出監(jiān)牢的瞬間惩系,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工如筛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堡牡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓杨刨,卻偏偏與公主長(zhǎng)得像晤柄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妖胀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 一個(gè)人走得快赚抡,一個(gè)人到底能走多遠(yuǎn)爬坑? 《知識(shí)變現(xiàn)》這本書(shū)開(kāi)啟了的自由工作之路,主要講述了自由工作者知識(shí)變現(xiàn)涂臣,建立個(gè)人...
    不忘初心_42f4閱讀 457評(píng)論 0 0
  • “九”是一個(gè)奇數(shù)盾计,古代以奇數(shù)為陽(yáng),偶數(shù)為陰赁遗。而“九”是最大的奇數(shù)署辉,故視“九”為極陽(yáng)數(shù)。古代帝王的宮殿多以九來(lái)裝飾岩四,...
    工程寶閱讀 646評(píng)論 0 0
  • 在一個(gè)微信群里,我結(jié)識(shí)一位90后妹妹末捣,她有自己的本職工作侠姑,在公號(hào)寫(xiě)作上幾乎日更的速度,每天一篇原創(chuàng)文章箩做,約3000...
    成長(zhǎng)馨路閱讀 479評(píng)論 0 1