1. 程式人生 > >iOS幾種常用執行緒鎖學習與總結。

iOS幾種常用執行緒鎖學習與總結。

開始前,先建立3個執行緒執行的任務。
- (void)method1 {
    NSLog(@"%@", @"執行緒1");
}

- (void)method2 {
    NSLog(@"%@", @"執行緒2");
}

- (void)method3 {
    NSLog(@"%@", @"執行緒3");
}

1.使用NSLock實現的鎖

NSLock是Cocoa提供給我們最基本的鎖物件,這也是我們經常所使用的,除lock和unlock方法外,NSLock還提供了tryLock和lockBeforeDate:兩個方法,前一個方法會嘗試加鎖,如果鎖不可用(已經被鎖住),剛並不會阻塞執行緒,並返回NO。lockBeforeDate:方法會在所指定Date之前嘗試加鎖,如果在指定時間之前都不能加鎖,則返回NO。

    ///NSLock鎖
    NSLock *lock = [[NSLock alloc] init];

    //執行緒1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        [self method1];
        sleep(10);
        [lock unlock];
    });
    //執行緒2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);//以保證讓執行緒2的程式碼後執行
        [lock lock];
        [self method2];
        [lock unlock];
    });

執行列印結果:

2018-05-26 09:07:00.966149+0800 AAAA[23178:827090] 執行緒1

2018-05-26 09:07:10.968050+0800 AAAA[23178:827091] 執行緒2

通過列印結果可以看到,加鎖的執行緒1在執行完整個任務後,執行緒2才開始執行。

如果加鎖後,鎖沒有及時釋放掉,則會造成死鎖現象,當執行緒1執行之後,其他執行緒將無法執行。

NSLock鎖較為常用,通常是新增在一個執行緒中,要注意的是新增鎖的執行緒不要是多次執行的,因為新增鎖之後,其他執行緒要等待鎖執行之後才能執行,所以新增鎖的的程式碼最好是不耗時的。

2.使用synchronized關鍵字構建的鎖

@synchronized指令實現鎖的優點就是我們不需要在程式碼中顯式的建立鎖物件,便可以實現鎖的機制,但作為一種預防措施,@synchronized塊會隱式的新增一個異常處理例程來保護程式碼,該處理例程會在異常丟擲的時候自動的釋放互斥鎖。所以如果不想讓隱式的異常處理例程帶來額外的開銷,你可以考慮使用鎖物件。

    UIView *other = [[UIView alloc] init];
    //執行緒1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized(self){
            [self method1];
            sleep(10);
        }
    });
    //執行緒2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        @synchronized(other){
            [self method2];
        }
    });
    //執行緒3
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);
        @synchronized(self){
            [self method3];
        }
    });

執行列印結果:

2018-05-26 09:15:03.896815+0800 AAAA[23309:843305] 執行緒1

2018-05-26 09:15:04.901968+0800 AAAA[23309:843304] 執行緒2

2018-05-26 09:15:13.902446+0800 AAAA[23309:843307] 執行緒3

通過結果可以看出,@synchronized鎖對應的為同一個物件時,鎖才會生效,當物件為self時,鎖的效果執行,執行緒阻塞,當物件為other時和self的鎖不衝突,可以同步執行。相比於NSLock,它的建立更為簡介,並且可以執行多條執行緒的加鎖。

3.使用C語言的pthread_mutex_t實現的鎖

    __block pthread_mutex_t mutex;
    __block pthread_mutex_t mutex2;
    pthread_mutex_init(&mutex, NULL);
    //執行緒1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&mutex);
        [self method1];
        sleep(5);
        pthread_mutex_unlock(&mutex);
    });
    //執行緒2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        pthread_mutex_lock(&mutex2);
        [self method2];
        pthread_mutex_unlock(&mutex2);
    });
    //執行緒3
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);
        pthread_mutex_lock(&mutex);
        [self method3];
        pthread_mutex_unlock(&mutex);
    });

執行列印結果:

2018-05-26 09:31:33.218339+0800 AAAA[23561:869507] 執行緒1

2018-05-26 09:31:34.219542+0800 AAAA[23561:869506] 執行緒2

2018-05-26 09:31:38.223177+0800 AAAA[23561:869508] 執行緒3

pthread_mutex_t定義在pthread.h,所以記得#include <pthread.h>,pthread_mutex_t鎖執行的邏輯與@synchronized類似,需要建立鎖的物件,mutex和mutex2為兩個鎖物件,每個執行緒只和同一個mutex產生鎖的效果。

4.使用GCD來實現的”鎖” 

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    //執行緒1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [self method1];
        sleep(10);
        dispatch_semaphore_signal(semaphore);
    });
    //執行緒2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [self method2];
        dispatch_semaphore_signal(semaphore);
    });

GCD鎖執行列印的效果與C語言的執行是一樣的,書寫方法的不同而已。

5.NSRecursiveLock遞迴鎖    

    NSLock *theLock = [[NSLock alloc] init];
    //執行緒1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        static void(^TestMethod)(int);

        TestMethod = ^(int value) {
            [theLock lock];
            if (value > 0)
            {
                [self method1];

                sleep(4);

                TestMethod(value-1);
            }
            [theLock unlock];
        };
        TestMethod(5);
    });
    //執行緒2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        [theLock lock];
        [self method2];
        [theLock unlock];
    });

執行列印結果:

2018-05-26 09:40:55.891359+0800 AAAA[23673:888329] 執行緒1

此現象,為死鎖現象,為什麼會產生這種情況,因為線上程1中,每次迴圈之後,都重新block回撥執行一次執行緒1的任務,theLock進行了多次的鎖lock,自己把自己給鎖住了,產生了死鎖現象,這裡就要引出NSRecursiveLock遞迴鎖了。上面的程式碼中如果將theLock的型別NSLock替換為NSRecursiveLock遞迴鎖,就不會產生死鎖了。

執行列印結果:

2018-05-26 09:44:19.823501+0800 AAAA[23727:894828] 執行緒1

2018-05-26 09:44:23.829966+0800 AAAA[23727:894828] 執行緒1

2018-05-26 09:44:27.837278+0800 AAAA[23727:894828] 執行緒1

2018-05-26 09:44:31.843270+0800 AAAA[23727:894828] 執行緒1

2018-05-26 09:44:35.849868+0800 AAAA[23727:894828] 執行緒1

2018-05-26 09:44:39.853977+0800 AAAA[23727:894829] 執行緒2

NSRecursiveLock類定義的鎖可以在同一執行緒多次lock,而不會造成死鎖。遞迴鎖會跟蹤它被多少次lock。每次成功的lock都必須平衡呼叫unlock操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其他執行緒獲得。

6.NSConditionLock條件鎖

線上程中,有時需要對應的條件去啟動某一個執行緒的任務,這時NSLock不一定能滿足我們的條件,這個時候需要NSConditionLock條件鎖來滿足我們。

正如NSConditionLock的命名一樣,在加鎖的時候我們可以額外新增對應的條件來控制我們的執行緒開啟。

    NSConditionLock *theLock = [[NSConditionLock alloc] init];
    //執行緒1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        for (int i=0;i<=3;i++)
        {
            [theLock lock];

            NSLog(@"thread1:%d",i);

            sleep(2);

            [theLock unlockWithCondition:i];
        }
    });

    //執行緒2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [theLock lockWhenCondition:2];

        NSLog(@"thread2");

        [theLock unlock];

    });
    //執行緒3
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [theLock lockWhenCondition:3];

        NSLog(@"thread3");

        [theLock unlock];

    });

執行列印結果:

2018-05-26 09:53:30.565448+0800 AAAA[23875:912929] thread1:0

2018-05-26 09:53:32.570947+0800 AAAA[23875:912929] thread1:1

2018-05-26 09:53:34.575138+0800 AAAA[23875:912929] thread1:2

2018-05-26 09:53:36.580697+0800 AAAA[23875:912929] thread1:3

2018-05-26 09:53:38.582872+0800 AAAA[23875:912930] thread3

這裡有兩個地方要注意,1.首先是線上程1中,我們添加了條件鎖的入口

[theLock unlockWithCondition:i];

而線上程2執行緒3中我們同時設定了兩個接收條件的鎖。i=2和i=3,為什麼thread2沒有執行,是因為條件鎖,在整個執行完之後才會去觸發他的條件,執行完整個任務後i=3所以只觸發了執行緒3的任務。那我們如果把執行緒2的觸發條件改成i=3,我們執行的結果為

2018-05-26 09:57:58.681922+0800 AAAA[23989:921838] thread1:0

2018-05-26 09:58:00.687457+0800 AAAA[23989:921838] thread1:1

2018-05-26 09:58:02.689869+0800 AAAA[23989:921838] thread1:2

2018-05-26 09:58:04.694757+0800 AAAA[23989:921838] thread1:3

2018-05-26 09:58:06.700193+0800 AAAA[23989:921836] thread2

2018-05-26 09:58:06.700547+0800 AAAA[23989:921839] thread3

這也就證明了,條件鎖對應的鎖是1對多的關係,如果用多個執行緒同時滿足了執行緒1結束時的條件,就會同時執行。

以上是我們常用到的執行緒鎖的使用,以及其對應的一些規則。如果有不完整的地方歡迎留言指教。


相關推薦

iOS常用執行學習總結

開始前,先建立3個執行緒執行的任務。- (void)method1 { NSLog(@"%@", @"執行緒1"); } - (void)method2 { NSLog(@"%@", @"執行緒2"); } - (void)method3 { NS

執行學習總結

單執行緒:        只有一個順序執行流        例如:單執行緒的程式如同只僱傭一個服務員的餐廳,他必須做完一件事情後才可以做下一件事情; 多執行緒:        可以

百度面試題之 啟動執行方式有哪執行池有哪

1、啟動執行緒方式: 要啟動的可以分為兩類:返回結果和不返回結果。對於這兩種,也分別有兩種啟動執行緒的方式: 1)繼承Thread類,implements Runnable介面 2)實現Callable介面通過FutureTask包裝器來建立Thread執行緒、使用Ex

IOS執行詳解

iOS的三種多執行緒技術 1.NSThread 每個NSThread物件對應一個執行緒,量級較輕(真正的多執行緒) 2.以下兩點是蘋果專門開發的“併發”技術,使得程式設計師可以不再去關心執行緒的具體使用問題 ØNSOperation/NSOperationQueu

總結建立執行的方式

//第一種: new Thread(){ public void run(){ System.out.println("haha"); } }.start(); //第二種: new Thread(new Runnable(){ @Override public void

鹿鼎記 · 韋小寶,麗春院、天地會、入皇宮等五個場景的惡搞版多執行學習

![](https://img-blog.csdnimg.cn/20201123093010245.png) 作者:小傅哥 部落格:[https://bugstack.cn](https://bugstack.cn) Github:[https://github.com/fuzhengwei/CodeGuid

Java多執行——概念優化

為了效能與使用的場景,Java實現鎖的方式有非常多。而關於鎖主要的實現包含synchronized關鍵字、AQS框架下的鎖,其中的實現都離不開以下的策略。 悲觀鎖與樂觀鎖 樂觀鎖。樂觀的想法,認為併發讀多寫少。每次操作的時候都不上鎖,直到更新的時候才通過CAS判斷更新。對於AQS框架下的鎖,初始就是

常見DRL(深度強化學習)方法總結對比之前提基本概念

版權宣告:本文為博主原創文章,未經博主允許不得轉載。                    https://blog.csdn.net/FrankieHello/article/details/78821488                 從今年的九月份到現在,接觸機器學

常用加密手段的加密解密

網路中傳輸敏感資訊的時候通常會對字串做加密解密處理1.Base64位加密(可加密解密)    最簡單的加密方式,沒有金鑰,這種方式只要讓別人拿到你的密文,就可以直接解密,只能用來迷惑,一般情況下不單獨使用,因為真的並沒有什麼卵用~可以和其他加密方式混合起來,作為一層外部包裝。import base64data

C#中常用的處理字串的方法總結

主要有以下幾種: string[i]所獲得的元素是隻讀的。 string.ToCharArray() (把string獲得一個可寫的字元陣列) string.Replace(old, new)(將字串中指定字元或者子字串做相應的替換) string.ToLower

Java多執行程式設計學習實踐

怎麼樣才算得上熟悉多執行緒程式設計?第一,明白程序和執行緒的基本概念第二,明白保護執行緒安全的基本方法有哪些第三,明白這些執行緒安全的方法,包括互斥鎖,自旋鎖,無鎖程式設計的適用的業務場景是什麼?從OS和硬體角度說說原理是怎麼樣的?開銷在哪裡?第四,能在現場藉助cas操作,風

4-5 執行安全性-有序性總結

有序性 一個執行緒觀察其他執行緒中的指令執行順序,由於指令重排序的存在,該觀察結果一般雜亂無序。 JMM允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單執行緒程式的執行,卻會影響到多執行緒併發執行的正確性。 可以通過volatile、synchro

spring三常用注入方式的測試總結

spring三種常用注入方式 setter方法注入 欄位注入(註解實現) 構造器注入 1、setter方法注入 建立一個介面: public interface Axe { public String chop();

高併發程式設計系列:4常用Java執行的特點,效能比較、使用場景

高併發程式設計系列:4種常用Java執行緒鎖的特點,效能比較、使用場景 http://youzhixueyuan.com/4-kinds-of-java-thread-locks.html   在Java併發程式設計中,經常遇到多個執行緒訪問同一個 共享資源 ,這時候作為開發者

4常用Java執行的特點,效能比較、使用場景

文章目錄 多執行緒的緣由 多執行緒併發面臨的問題 4種Java執行緒鎖(執行緒同步) Java執行緒鎖總結 多執行緒的緣由 在出現了程序之後,作業系統的效能得到了大大的提升。雖然程序的出現解決了作業系統的併

java執行常用方法

join() Thread物件方法,用於使當前執行緒和後面的程式碼同步,使當前執行緒內操作執行完成後再執行後面的指令 wait() Thread物件方法,使當前執行緒進入等待狀態,等待狀態的執行緒不會去競爭資源 sleep() Thread類方法,使當前執行緒休眠指定時間

iOS NSthread & Thread 開啟執行方式

一、開啟執行緒執行指定物件的方法 /** 引數1: 執行引數2方法的物件 引數2: 開啟執行緒後執行的方法 引數3: 傳遞的物件資料(引數2的方法可以直接用) */ // OC - (

常用執行池threadpool

我們知道一個系統一般不可能只有一個執行緒,而根據系統的伺服器等硬體水平,我們可以合理的利用多執行緒來快速的完成我們所需要的功能。而對於系統而言,如果頻繁的建立和銷燬執行緒,也會給系統帶來相當大的負擔,所以我們平時都是利用執行緒池來解決這一點。執行緒池的作用:1)減少建立和銷燬

Java中實現執行同步的常用方式

首先講一下為什麼要實現執行緒同步: java允許多執行緒併發控制,當多個執行緒同時操作一個可共享的資源變數時(如資料的增刪改查),  將會導致資料不準確,相互之間產生衝突,因此加入同步鎖以避免在該執行緒沒有完成操作之前,被其他執行緒的呼叫, 從而保證了該變數的唯一性和準

4常用Java執行的特點,效能比較及使用場景

開發十年,就只剩下這套架構體系了! >>>