线程安全

线程安全问题

1,单线程情况下,任务依次执行不会出现线程安全的问题;
2,单线程情况下,多线程都是访问共享资源,而不是修改资源也可以保证线程安全,如:只读的全局属性;
3,线程不安全是由于多线程访问造成的,多线程访问和修改共享资源引起了不可预测的结果;

线程锁

线程锁可以有效的解决多线程引起的安全问题.
iOS多线程开发为保证线程安全而使用了好几种锁:

1
2
NSLock、dispatch_semaphore、NSCondition  
NSRecursiveLock、NSConditionLock、@synchronized
NSLock
1
2
3
4
5
_lock = [[NSLock alloc]init];//创建
[_lock lock];//锁住
//执行代码区域

[_lock unlock];//解锁
@synchronized

一个对象层面的锁,锁住整个对象,底层使用了互斥递归锁来实现;
常用在单例的创建

1
2
3
4
5
6
7
8
9
+(Common *)sharedInstance{
static Common *common = nil;
@synchronized (common) {
if (common == nil) {
common = [[Common alloc]init];
}
}
return common;
}

模拟场景:

火车站某趟列车的票数销售.
说明:总票数固定,会出现同时在销售的情况,但是销售的余额票数不能构成混乱;

NSLock为例
不使用线程锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    dispatch_queue_t q1 = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
_lock = [[NSLock alloc]init];
_allCount = 20;//火车站总票数
for (NSUInteger i = 0; i < 5; i++) {//开启5个线程
dispatch_async(q1, ^{
//模拟不同销售途径来销售火车票,火车票总数是固定,不能造成票数混乱;
while (1) {
// [_lock lock];
if (_allCount > 0) {
_allCount --;
NSLog(@"当前线程(销售途径):%@,当前票数:%lu",[NSThread currentThread],(unsigned long)_allCount);
}else{
[_lock unlock];
return;
}
// [_lock unlock];
[NSThread sleepForTimeInterval:0.5];//当前线程休眠0.5秒
}
});
}

打印日志:数据造成各种混乱
2020-04-10 11:34:19.218613+0800 Dispatch(线程安全)[27226:1143704] 当前线程(销售途径):<NSThread: 0x600003d19880>{number = 4, name = (null)},当前票数:18
2020-04-10 11:34:19.218694+0800 Dispatch(线程安全)[27226:1143702] 当前线程(销售途径):<NSThread: 0x600003d149c0>{number = 6, name = (null)},当前票数:19
2020-04-10 11:34:19.218746+0800 Dispatch(线程安全)[27226:1143711] 当前线程(销售途径):<NSThread: 0x600003d0a440>{number = 7, name = (null)},当前票数:16
2020-04-10 11:34:19.218746+0800 Dispatch(线程安全)[27226:1143703] 当前线程(销售途径):<NSThread: 0x600003d1e400>{number = 5, name = (null)},当前票数:17
2020-04-10 11:34:19.220135+0800 Dispatch(线程安全)[27226:1143705] 当前线程(销售途径):<NSThread: 0x600003d158c0>{number = 8, name = (null)},当前票数:15
2020-04-10 11:34:19.720263+0800 Dispatch(线程安全)[27226:1143702] 当前线程(销售途径):<NSThread: 0x600003d149c0>{number = 6, name = (null)},当前票数:14
2020-04-10 11:34:19.720263+0800 Dispatch(线程安全)[27226:1143704] 当前线程(销售途径):<NSThread: 0x600003d19880>{number = 4, name = (null)},当前票数:14
2020-04-10 11:34:19.720262+0800 Dispatch(线程安全)[27226:1143711] 当前线程(销售途径):<NSThread: 0x600003d0a440>{number = 7, name = (null)},当前票数:13
2020-04-10 11:34:19.722770+0800 Dispatch(线程安全)[27226:1143703] 当前线程(销售途径):<NSThread: 0x600003d1e400>{number = 5, name = (null)},当前票数:12
2020-04-10 11:34:19.722770+0800 Dispatch(线程安全)[27226:1143705] 当前线程(销售途径):<NSThread: 0x600003d158c0>{number = 8, name = (null)},当前票数:12
2020-04-10 11:34:20.224261+0800 Dispatch(线程安全)[27226:1143711] 当前线程(销售途径):<NSThread: 0x600003d0a440>{number = 7, name = (null)},当前票数:11
2020-04-10 11:34:20.224261+0800 Dispatch(线程安全)[27226:1143702] 当前线程(销售途径):<NSThread: 0x600003d149c0>{number = 6, name = (null)},当前票数:10
2020-04-10 11:34:20.224261+0800 Dispatch(线程安全)[27226:1143704] 当前线程(销售途径):<NSThread: 0x600003d19880>{number = 4, name = (null)},当前票数:9
2020-04-10 11:34:20.225632+0800 Dispatch(线程安全)[27226:1143703] 当前线程(销售途径):<NSThread: 0x600003d1e400>{number = 5, name = (null)},当前票数:8
2020-04-10 11:34:20.225670+0800 Dispatch(线程安全)[27226:1143705] 当前线程(销售途径):<NSThread: 0x600003d158c0>{number = 8, name = (null)},当前票数:7
2020-04-10 11:34:20.729091+0800 Dispatch(线程安全)[27226:1143702] 当前线程(销售途径):<NSThread: 0x600003d149c0>{number = 6, name = (null)},当前票数:5
2020-04-10 11:34:20.729038+0800 Dispatch(线程安全)[27226:1143704] 当前线程(销售途径):<NSThread: 0x600003d19880>{number = 4, name = (null)},当前票数:6
2020-04-10 11:34:20.729094+0800 Dispatch(线程安全)[27226:1143711] 当前线程(销售途径):<NSThread: 0x600003d0a440>{number = 7, name = (null)},当前票数:4
2020-04-10 11:34:20.729128+0800 Dispatch(线程安全)[27226:1143705] 当前线程(销售途径):<NSThread: 0x600003d158c0>{number = 8, name = (null)},当前票数:3
2020-04-10 11:34:20.729128+0800 Dispatch(线程安全)[27226:1143703] 当前线程(销售途径):<NSThread: 0x600003d1e400>{number = 5, name = (null)},当前票数:2
2020-04-10 11:34:21.230992+0800 Dispatch(线程安全)[27226:1143711] 当前线程(销售途径):<NSThread: 0x600003d0a440>{number = 7, name = (null)},当前票数:0
2020-04-10 11:34:21.230992+0800 Dispatch(线程安全)[27226:1143704] 当前线程(销售途径):<NSThread: 0x600003d19880>{number = 4, name = (null)},当前票数:0

使用线程锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    dispatch_queue_t q1 = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
_lock = [[NSLock alloc]init];
_allCount = 20;//火车站总票数
for (NSUInteger i = 0; i < 5; i++) {//开启5个线程
dispatch_async(q1, ^{
//模拟不同销售途径来销售火车票,火车票总数是固定,不能造成票数混乱;
while (1) {
[_lock lock];
if (_allCount > 0) {
_allCount --;
NSLog(@"当前线程(销售途径):%@,当前票数:%lu",[NSThread currentThread],(unsigned long)_allCount);
}else{
[_lock unlock];
return;
}
[_lock unlock];
[NSThread sleepForTimeInterval:0.5];//当前线程休眠0.5秒
}
});
}
日志:数据正常
2020-04-10 11:37:38.510142+0800 Dispatch(线程安全)[27257:1146071] 当前线程(销售途径):<NSThread: 0x6000010e0900>{number = 5, name = (null)},当前票数:19
2020-04-10 11:37:38.510377+0800 Dispatch(线程安全)[27257:1146069] 当前线程(销售途径):<NSThread: 0x600001093640>{number = 4, name = (null)},当前票数:18
2020-04-10 11:37:38.510551+0800 Dispatch(线程安全)[27257:1146070] 当前线程(销售途径):<NSThread: 0x6000010ee380>{number = 6, name = (null)},当前票数:17
2020-04-10 11:37:38.510758+0800 Dispatch(线程安全)[27257:1146077] 当前线程(销售途径):<NSThread: 0x6000010fae00>{number = 7, name = (null)},当前票数:16
2020-04-10 11:37:38.510920+0800 Dispatch(线程安全)[27257:1146067] 当前线程(销售途径):<NSThread: 0x60000109bc80>{number = 8, name = (null)},当前票数:15
2020-04-10 11:37:39.015058+0800 Dispatch(线程安全)[27257:1146069] 当前线程(销售途径):<NSThread: 0x600001093640>{number = 4, name = (null)},当前票数:14
2020-04-10 11:37:39.015466+0800 Dispatch(线程安全)[27257:1146071] 当前线程(销售途径):<NSThread: 0x6000010e0900>{number = 5, name = (null)},当前票数:13
2020-04-10 11:37:39.015826+0800 Dispatch(线程安全)[27257:1146070] 当前线程(销售途径):<NSThread: 0x6000010ee380>{number = 6, name = (null)},当前票数:12
2020-04-10 11:37:39.016035+0800 Dispatch(线程安全)[27257:1146077] 当前线程(销售途径):<NSThread: 0x6000010fae00>{number = 7, name = (null)},当前票数:11
2020-04-10 11:37:39.016242+0800 Dispatch(线程安全)[27257:1146067] 当前线程(销售途径):<NSThread: 0x60000109bc80>{number = 8, name = (null)},当前票数:10
2020-04-10 11:37:39.519114+0800 Dispatch(线程安全)[27257:1146070] 当前线程(销售途径):<NSThread: 0x6000010ee380>{number = 6, name = (null)},当前票数:9
2020-04-10 11:37:39.519408+0800 Dispatch(线程安全)[27257:1146071] 当前线程(销售途径):<NSThread: 0x6000010e0900>{number = 5, name = (null)},当前票数:8
2020-04-10 11:37:39.519728+0800 Dispatch(线程安全)[27257:1146067] 当前线程(销售途径):<NSThread: 0x60000109bc80>{number = 8, name = (null)},当前票数:7
2020-04-10 11:37:39.520054+0800 Dispatch(线程安全)[27257:1146069] 当前线程(销售途径):<NSThread: 0x600001093640>{number = 4, name = (null)},当前票数:6
2020-04-10 11:37:39.520262+0800 Dispatch(线程安全)[27257:1146077] 当前线程(销售途径):<NSThread: 0x6000010fae00>{number = 7, name = (null)},当前票数:5
2020-04-10 11:37:40.020898+0800 Dispatch(线程安全)[27257:1146071] 当前线程(销售途径):<NSThread: 0x6000010e0900>{number = 5, name = (null)},当前票数:4
2020-04-10 11:37:40.021315+0800 Dispatch(线程安全)[27257:1146070] 当前线程(销售途径):<NSThread: 0x6000010ee380>{number = 6, name = (null)},当前票数:3
2020-04-10 11:37:40.021709+0800 Dispatch(线程安全)[27257:1146067] 当前线程(销售途径):<NSThread: 0x60000109bc80>{number = 8, name = (null)},当前票数:2
2020-04-10 11:37:40.021944+0800 Dispatch(线程安全)[27257:1146069] 当前线程(销售途径):<NSThread: 0x600001093640>{number = 4, name = (null)},当前票数:1
2020-04-10 11:37:40.022108+0800 Dispatch(线程安全)[27257:1146077] 当前线程(销售途径):<NSThread: 0x6000010fae00>{number = 7, name = (null)},当前票数:0