1??創(chuàng)建和啟動線程簡單說明
一個(gè)NSThread對象就代表一條線程
創(chuàng)建、啟動線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
// 線程一啟動秦效,就會在線程thread中執(zhí)行self的run方法
主線程相關(guān)用法
+ (NSThread *)mainThread; // 獲得主線程
- (BOOL)isMainThread; // 是否為主線程
+ (BOOL)isMainThread; // 是否為主線程
其他用法
//獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
//線程的調(diào)度優(yōu)先級:調(diào)度優(yōu)先級的取值范圍是0.0 ~ 1.0,默認(rèn)0.5捅膘,值越大郁惜,優(yōu)先級越高
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
//設(shè)置線程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
創(chuàng)建線程后自動啟動線程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 隱式創(chuàng)建并啟動線程
[self performSelectorInBackground:@selector(run) withObject:nil];
//上述2種創(chuàng)建線程方式的優(yōu)缺點(diǎn)
//優(yōu)點(diǎn):簡單快捷
//缺點(diǎn):無法對線程進(jìn)行更詳細(xì)的設(shè)置
2??線程安全
一、多線程的安全隱患
資源共享
- 1塊資源可能會被多個(gè)線程共享趾断,也就是多個(gè)線程可能會訪問同一塊資源
- 比如多個(gè)線程訪問同一個(gè)對象、同一個(gè)變量吩愧、同一個(gè)文件
- 當(dāng)多個(gè)線程訪問同一塊資源時(shí)芋酌,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題
示例一:
示例二:
問題代碼:
#import "YYViewController.h"
11
12 @interface YYViewController ()
13 //剩余票數(shù)
14
15 @property(nonatomic,assign) int leftTicketsCount;
16 @property(nonatomic,strong)NSThread *thread1;
17 @property(nonatomic,strong)NSThread *thread2;
18 @property(nonatomic,strong)NSThread *thread3;
19
20
21 @end
22
23
24 @implementation YYViewController
25
26
27 - (void)viewDidLoad
28 {
29 [super viewDidLoad];
30
31 //默認(rèn)有20張票
32
33 self.leftTicketsCount=10;
34
35 //開啟多個(gè)線程,模擬售票員售票
36
37 self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
38
39 self.thread1.name=@"售票員A";
40
41 self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
42
43 self.thread2.name=@"售票員B";
44
45 self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
46 self.thread3.name=@"售票員C";
47 }
48
49
50 -(void)sellTickets
51 {
52 while (1) {
53 //1.先檢查票數(shù)
54 int count=self.leftTicketsCount;
55 if (count>0) {
56 //暫停一段時(shí)間
57 [NSThread sleepForTimeInterval:0.002];
58
59 //2.票數(shù)-1
60 self.leftTicketsCount= count-1;
61
62 //獲取當(dāng)前線程
63 NSThread *current=[NSThread currentThread];
64 NSLog(@"%@--賣了一張票雁佳,還剩余%d張票",current,self.leftTicketsCount);
65 }else
66 {
67 //退出線程
68 [NSThread exit];
69 }
70 }
71 }
72
73
74 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
75 {
76 //開啟線程
77
78 [self.thread1 start];
79 [self.thread2 start];
80 [self.thread3 start];
81
82 }
83
84 @end
打印結(jié)果:
image
二脐帝、安全隱患分析
三、如何解決
- 互斥鎖使用格式
- @synchronized(鎖對象) { // 需要鎖定的代碼 }
- 注意:鎖定1份代碼只用1把鎖糖权,用多把鎖是無效的
43 -(void)sellTickets
44 {
45 while (1) {
46 @synchronized(self){//只能加一把鎖
47 //1.先檢查票數(shù)
48
49 int count=self.leftTicketsCount;
50 if (count>0) {
51 //暫停一段時(shí)間
52 [NSThread sleepForTimeInterval:0.002];
53 //2.票數(shù)-1
54
55 self.leftTicketsCount= count-1;
56 //獲取當(dāng)前線程
57 NSThread *current=[NSThread currentThread];
58 NSLog(@"%@--賣了一張票堵腹,還剩余%d張票",current,self.leftTicketsCount);
59
60 }else
61 {
62 //退出線程
63 [NSThread exit];
64 }
65 }
66 }
67 }
執(zhí)行效果圖
互斥鎖的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
- 缺點(diǎn):需要消耗大量的CPU資源
- 互斥鎖的使用前提:多條線程搶奪同一塊資源
- 相關(guān)專業(yè)術(shù)語:線程同步,多條線程按順序地執(zhí)行任務(wù)
- 互斥鎖,就是使用了線程同步技術(shù)
四:原子和非原子屬性
- OC在定義屬性時(shí)有nonatomic和atomic兩種選擇
- atomic:原子屬性星澳,為setter方法加鎖(默認(rèn)就是atomic)
- nonatomic:非原子屬性秸滴,不會為setter方法加鎖
- atomic加鎖原理
1 @property (assign, atomic) int age;
2
3 - (void)setAge:(int)age
4 {
5
6 @synchronized(self) {
7 _age = age;
8 }
9 }
原子和非原子屬性的選擇
- nonatomic和atomic對比
- atomic:線程安全,需要消耗大量的資源
- nonatomic:非線程安全募判,適合內(nèi)存小的移動設(shè)備
iOS開發(fā)的建議
- 所有屬性都聲明為nonatomic
- 盡量避免多線程搶奪同一塊資源
- 盡量將加鎖荡含、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動客戶端的壓力
3??線程間通信
線程間通信:在1個(gè)進(jìn)程中届垫,線程往往不是孤立存在的释液,多個(gè)線程之間需要經(jīng)常進(jìn)行通信
線程間通信的體現(xiàn)
1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
在1個(gè)線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
線程間通信常用方法
(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
線程間通信示例 – 圖片下載
12 @interface YYViewController ()
13 @property (weak, nonatomic) IBOutlet UIImageView *iconView;
14 @end
15
16 @implementation YYViewController
17
18 - (void)viewDidLoad
19 {
20 [super viewDidLoad];
21 }
22
23
24 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
25 {
26 // 在子線程中調(diào)用download方法下載圖片
27
28 [self performSelectorInBackground:@selector(download) withObject:nil];
29 }
30
31
32 -(void)download
33 {
34
35 //1.根據(jù)URL下載圖片
36 //從網(wǎng)絡(luò)中下載圖片
37 NSURL *urlstr=[NSURL URLWithString:@"fdsf"];
38
39 //把圖片轉(zhuǎn)換為二進(jìn)制的數(shù)據(jù)
40 NSData *data=[NSData dataWithContentsOfURL:urlstr];//這一行操作會比較耗時(shí)
41
42 //把數(shù)據(jù)轉(zhuǎn)換成圖片
43 UIImage *image=[UIImage imageWithData:data];
44
45 //2.回到主線程中設(shè)置圖片
46 //第一種方式
47 // [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
48
49 //第二種方式
50 // [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
51
52 //第三種方式
53 [self.iconView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
54 }
55
56
57 //設(shè)置顯示圖片
58 //-(void)settingImage:(UIImage *)image
59 //{
60 // self.iconView.image=image;
61 //}
62
63 @end