多線程學(xué)習(xí)筆記 - NSThread
NSThread的集中創(chuàng)建方法
1.方法一: NSThread對(duì)象創(chuàng)建 需要手動(dòng)開(kāi)啟線程
// 線程創(chuàng)建
NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(test_1) object:@"對(duì)象線程方法"];
// 開(kāi)啟線程
[thread start];
// 方法
- (void) test_1
{
NSLog(@"test_1 - %@",[NSThread currentThread]);
//NSThread練習(xí)[1608:289527] test_1 - <NSThread: 0x600000262c40>{number = 3, name = (null)}
}
2.方法二: 分離子線程, 會(huì)自動(dòng)啟動(dòng)線程
// 線程創(chuàng)建
[NSThread detachNewThreadSelector:@selector(test_2) toTarget:self withObject:@"分離線程"];
// 方法
- (void) test_2
{
NSLog(@"test_2 - %@",[NSThread currentThread]);
//NSThread練習(xí)[1608:289528] test_2 - <NSThread: 0x600000263640>{number = 4, name = (null)}
}
3.方法三: 開(kāi)啟一條后臺(tái)線程, 也會(huì)自動(dòng)啟動(dòng)線程
// 線程創(chuàng)建
[self performSelectorInBackground:@selector(test_3) withObject:@"后臺(tái)程序"];
// 方法
- (void) test_3
{
NSLog(@"test_3 - %@",[NSThread currentThread]);
//2017-06-22 13:55:50.292 NSThread練習(xí)[1608:289529] test_3 - <NSThread: 0x600000263800>{number = 5, name = (null)}
}
方法對(duì)比
方法一
- 優(yōu)點(diǎn): 可以拿到線程對(duì)象, 并設(shè)置相關(guān)屬性
- 缺點(diǎn): 代碼量相對(duì)多一點(diǎn), 需要手動(dòng)啟動(dòng)線程
方法二和方法三
- 優(yōu)點(diǎn): 創(chuàng)建線程簡(jiǎn)單快捷
- 缺點(diǎn): 無(wú)法拿到線程對(duì)象, 無(wú)法設(shè)置相關(guān)屬性
NSThread的線程名字name
與優(yōu)先級(jí)threadPriority
threadPriority 的取值范圍是 0.0 -- 1.0, 默認(rèn)是0.5. 數(shù)值越大, 優(yōu)先級(jí)越高 ,優(yōu)先級(jí)越高鹿蜀,處理器先處理這個(gè)線程的概率越大衩匣。
給線程添加名字與優(yōu)先級(jí)
NSThread * thread_1 = [[NSThread alloc] initWithTarget:self selector:@selector(compareTest) object:nil];
thread_1.name = @"線程——1"; // 線程名字
thread_1.threadPriority = 0.1; // 線程優(yōu)先級(jí)
[thread_1 start];
NSThread * thread_2 = [[NSThread alloc] initWithTarget:self selector:@selector(compareTest) object:nil];
thread_2.name = @"線程——2";
thread_2.threadPriority = 0.5;
[thread_2 start];
NSThread * thread_3 = [[NSThread alloc] initWithTarget:self selector:@selector(compareTest) object:nil];
thread_3.name = @"線程——3";
thread_3.threadPriority = 1.0;
[thread_3 start];
打印出線程的名字與順序分別是
// 優(yōu)先級(jí)高的線程先執(zhí)行
2017-06-22 14:19:00.461 NSThread練習(xí)[1690:379110] 當(dāng)前線程名字 - 線程——3
2017-06-22 14:19:00.461 NSThread練習(xí)[1690:379109] 當(dāng)前線程名字 - 線程——2
2017-06-22 14:19:00.461 NSThread練習(xí)[1690:379108] 當(dāng)前線程名字 - 線程——1
控制線程
- (void)start; // 開(kāi)啟線程
+ (void)exit; // 結(jié)束線程
// 阻塞(或休眠)線程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
線程一旦進(jìn)入到死亡狀態(tài), 線程也就停止了, 就不能再次啟動(dòng)任務(wù).
線程安全
1.一塊資源可能會(huì)被多個(gè)線程共享, 也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
2.比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象, 同一個(gè)變量, 同一個(gè)文件
3.當(dāng)多個(gè)線程訪問(wèn)同一塊資源時(shí), 很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題
賣票的多線程問(wèn)題(多線程共享一塊資源)
- 創(chuàng)建不同線程與票總數(shù)
// 總共100張票
self.ticketCount = 100;
NSThread * thread_A = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
NSThread * thread_B = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
NSThread * thread_C = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
thread_A.name = @"售票員A";
thread_B.name = @"售票員B";
thread_C.name = @"售票員C";
[thread_A start];
[thread_B start];
[thread_C start];
- 不使用線程鎖的方法
// 賣票
- (void) saleTicket
{
while (1) {
NSInteger count = self.ticketCount;
if (count > 0)
{
for (int i = 0; i < 1000000; ++i)
{
// 只是耗時(shí)間, 沒(méi)有其他用
}
self.ticketCount = count - 1;
NSLog(@"%@賣出一張票,還剩- %ld", [NSThread currentThread].name, (long)self.ticketCount);
} else {
NSLog(@"票賣完了");
break;
}
}
}
可以看出最后的票賣的很混亂
2017-06-22 15:15:55.212 NSThread練習(xí)[1968:555811] 售票員C賣出一張票,還剩- 2
2017-06-22 15:15:55.213 NSThread練習(xí)[1968:555810] 售票員B賣出一張票,還剩- 4
2017-06-22 15:15:55.214 NSThread練習(xí)[1968:555809] 售票員A賣出一張票,還剩- 2
2017-06-22 15:15:55.216 NSThread練習(xí)[1968:555811] 售票員C賣出一張票,還剩- 1
2017-06-22 15:15:55.217 NSThread練習(xí)[1968:555810] 售票員B賣出一張票,還剩- 3
2017-06-22 15:15:55.218 NSThread練習(xí)[1968:555809] 售票員A賣出一張票,還剩- 1
2017-06-22 15:15:55.221 NSThread練習(xí)[1968:555811] 售票員C賣出一張票,還剩- 0
2017-06-22 15:15:55.221 NSThread練習(xí)[1968:555811] 票賣完了
2017-06-22 15:15:55.222 NSThread練習(xí)[1968:555809] 售票員A賣出一張票,還剩- 0
2017-06-22 15:15:55.222 NSThread練習(xí)[1968:555809] 票賣完了
2017-06-22 15:15:55.223 NSThread練習(xí)[1968:555810] 售票員B賣出一張票,還剩- 2
2017-06-22 15:15:55.226 NSThread練習(xí)[1968:555810] 售票員B賣出一張票,還剩- 1
2017-06-22 15:15:55.229 NSThread練習(xí)[1968:555810] 售票員B賣出一張票,還剩- 0
2017-06-22 15:15:55.230 NSThread練習(xí)[1968:555810] 票賣完了
- 使用線程鎖保證線程安全
// 賣票
- (void) saleTicket
{
while (1) {
// 加 互斥鎖, 全局唯一, self, 代表鎖對(duì)象
@synchronized(self)
{
NSInteger count = self.ticketCount;
if (count > 0)
{
self.ticketCount = count - 1;
NSLog(@"%@賣出一張票,還剩- %ld", [NSThread currentThread].name, (long)self.ticketCount);
} else {
NSLog(@"票賣完了");
break;
}
}
}
}
//不會(huì)出現(xiàn)數(shù)據(jù)混亂的情況了, 也達(dá)到了三個(gè)線程賣票的功能.
2017-06-22 15:24:00.116 NSThread練習(xí)[1995:594897] 售票員A賣出一張票,還剩- 5
2017-06-22 15:24:00.117 NSThread練習(xí)[1995:594899] 售票員C賣出一張票,還剩- 4
2017-06-22 15:24:00.117 NSThread練習(xí)[1995:594898] 售票員B賣出一張票,還剩- 3
2017-06-22 15:24:00.117 NSThread練習(xí)[1995:594897] 售票員A賣出一張票,還剩- 2
2017-06-22 15:24:00.117 NSThread練習(xí)[1995:594899] 售票員C賣出一張票,還剩- 1
2017-06-22 15:24:00.118 NSThread練習(xí)[1995:594898] 售票員B賣出一張票,還剩- 0
2017-06-22 15:24:00.119 NSThread練習(xí)[1995:594897] 票賣完了
2017-06-22 15:24:00.119 NSThread練習(xí)[1995:594899] 票賣完了
2017-06-22 15:24:00.120 NSThread練習(xí)[1995:594898] 票賣完了