鎖的類別:互斥鎖,遞歸鎖,條件鎖,自旋鎖等
鎖的實現方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信號量等
下面說一下常用的幾種鎖:
1.@synchronized:對象級別所,互斥鎖,性能較差不推薦使用
@synchronized(這里添加一個OC對象,一般使用self) {
這里寫要加鎖的代碼
}
@synchronized使用注意點
1.加鎖的代碼盡量少
2.添加的OC對象必須在多個線程中都是同一對象,下面舉一個反例
- (void)viewDidLoad { [super viewDidLoad]; //設置票的數量為5 _tickets = 5; //線程一 NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; threadOne.name = @"threadOne"; //線程二 NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; //開啟線程 [threadOne start]; [threadTwo start]; } - (void)saleTickets { NSObject *object = [[NSObject alloc] init]; while (1) { @synchronized(object) { [NSThread sleepForTimeInterval:1]; if (_tickets > 0) { _tickets--; NSLog(@"剩余票數= %ld",_tickets); } else { NSLog(@"票賣完了"); break; } } } }
結果賣票又出錯了,出現這個原因的問題是每個線程都會創建一個object對象,鎖后面加的object在不同線程中就不同了;
把@synchronized(object)改成 @synchronized(self)就能得到了正確結果
2.NSLock:互斥鎖,
@interface ViewController () { NSLock *mutexLock; } @property (assign, nonatomic)NSInteger tickets; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //創建鎖 mutexLock = [[NSLock alloc] init]; //設置票的數量為5 _tickets = 5; //線程一 NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; threadOne.name = @"threadOne"; //線程二 NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; //開啟線程 [threadOne start]; [threadTwo start]; } - (void)saleTickets { while (1) { [NSThread sleepForTimeInterval:1]; //加鎖 [mutexLock lock]; if (_tickets > 0) { _tickets--; NSLog(@"剩余票數= %ld",_tickets); } else { NSLog(@"票賣完了"); break; } //解鎖 [mutexLock unlock]; } }
NSLock: 使用注意,不能多次調用 lock方法,會造成死鎖
3.NSRecursiveLock:遞歸鎖
@interface ViewController () { NSRecursiveLock *rsLock; } @property (assign, nonatomic)NSInteger tickets; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //創建鎖遞歸鎖 rsLock = [[NSRecursiveLock alloc] init]; //設置票的數量為5 _tickets = 5; //線程一 NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; threadOne.name = @"threadOne"; //線程二 NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil]; //開啟線程 [threadOne start]; [threadTwo start]; } - (void)saleTickets { while (1) { [NSThread sleepForTimeInterval:1]; //加鎖,遞歸鎖可以多次加鎖 [rsLock lock]; [rsLock lock]; if (_tickets > 0) { _tickets--; NSLog(@"剩余票數= %ld",_tickets); } else { NSLog(@"票賣完了"); break; } //解鎖,只有對應次數解鎖,其他線程才能訪問。 [rsLock unlock]; [rsLock unlock]; } }
4.NSConditionLock:條件鎖
NSConditionLock:條件鎖,一個線程獲得了鎖,其它線程等待。
[xxxx lock]; 表示 xxx 期待獲得鎖,如果沒有其他線程獲得鎖(不需要判斷內部的condition) 那它能執行此行以下代碼,如果已經有其他線程獲得鎖(可能是條件鎖,或者無條件鎖),則等待,直至其他線程解鎖
[xxx lockWhenCondition:A條件]; 表示如果沒有其他線程獲得該鎖,但是該鎖內部的condition不等於A條件,它依然不能獲得鎖,仍然等待。如果內部的condition等於A條件,並且沒有其他線程獲得該鎖,則進入代碼區,同時設置它獲得該鎖,其他任何線程都將等待它代碼的完成,直至它解鎖。
[xxx unlockWithCondition:A條件]; 表示釋放鎖,同時把內部的condition設置為A條件
@interface ViewController () { NSConditionLock *_cdtLock; //條件鎖 } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //創建條件鎖 _cdtLock = [[NSConditionLock alloc] init]; [NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil]; } - (void)conditionLockAction1 { //阻塞線程2s [NSThread sleepForTimeInterval:2]; for (NSInteger i = 0; i < 3; i++) { //加鎖 [_cdtLock lock]; NSLog(@"i = %li", i); //釋放鎖,並設置condition屬性的值為i [_cdtLock unlockWithCondition:i]; } } - (void)conditionLockAction2 { //當標識為2時同步代碼段才能夠執行,如果標識為其它數字則當前線程被阻塞。 [_cdtLock lockWhenCondition:2]; NSLog(@"thread2"); [_cdtLock unlock]; }
打印結果:
如果我們把代碼中[_cdtLock lockWhenCondition:2]換成[_cdtLock lockWhenCondition:1]則會發現出現如下結果
和我們預想的在i = 1后面打印thread2不符合,這是因為conditionLockAction1中的代碼段也需要獲得鎖,同時在循環執行過后把condition置成了2,那么conditionLockAction2就再也沒機會加鎖了,所以不打印thread2。
我們可以靠下面的代碼驗證
@interface ViewController () {
NSConditionLock *_cdtLock; //條件鎖
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//創建條件鎖
_cdtLock = [[NSConditionLock alloc] init];
[NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];
}
- (void)conditionLockAction1 {
//阻塞線程2s
[NSThread sleepForTimeInterval:2];
for (NSInteger i = 0; i < 3; i++) {
//加鎖
[_cdtLock lock];
NSLog(@"i = %li", i);
//釋放鎖,並設置condition屬性的值為i
[_cdtLock unlockWithCondition:i];
//在i 為 1的時候阻塞線程1s
if (i == 1)
{
[NSThread sleepForTimeInterval:1];
}
}
}
- (void)conditionLockAction2 {
//當標識為2時同步代碼段才能夠執行,如果標識為其它數字則當前線程被阻塞。
[_cdtLock lockWhenCondition:1];
NSLog(@"thread2");
[_cdtLock unlock];
}
現在的結果就和我們預期的一樣了
5.NSCondition:可以理解為互斥鎖和條件鎖的結合
用生產者消費者中的例子可以很好的理解NSCondition
1.生產者要取得鎖,然后去生產,生產后將生產的商品放入庫房,如果庫房滿了,則wait,就釋放鎖,直到其它線程喚醒它去生產,如果沒有滿,則生產商品后調用signal,可以喚醒在此condition上等待的線程。
2.消費者要取得鎖,然后去消費,如果當前沒有商品,則wait,釋放鎖,直到有線程去喚醒它消費,如果有商品,則消費后會通知正在等待的生產者去生產商品。
生產者和消費者的關鍵是:當庫房已滿時,生產者等待,不再繼續生產商品,當庫房已空時,消費者等待,不再繼續消費商品,走到庫房有商品時,會由生產者通知消費來消費。
- (void)conditionTest { //創建數組存放商品 products = [[NSMutableArray alloc] init]; condition = [[NSCondition alloc] init]; [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil]; } - (void)createConsumenr { while (1) { //模擬消費商品時間,讓它比生產慢一點 [NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 1.5]; [condition lock]; while (products.count == 0) { NSLog(@"商品為0,等待生產"); [condition wait]; } [products removeLastObject]; NSLog(@"消費了一個商品,商品數 = %ld",products.count); [condition signal]; [condition unlock]; } } - (void)createProducter { while (1) { //模擬生產商品時間 [NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 0.5]; [condition lock]; while (products.count == 5) { NSLog(@"商品滿了,等待消費"); [condition wait]; } [products addObject:[[NSObject alloc] init]]; NSLog(@"生產了一個商品,商品數%ld",products.count); [condition signal]; [condition unlock]; } }
了解死鎖
概念:死鎖是指兩個或兩個以上的進程(線程)在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。
產生死鎖的4個必要條件