鎖的類別:互斥鎖,遞歸鎖,條件鎖,自旋鎖等
鎖的實現方式: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個必要條件
