之前的項(xiàng)目中一直沒(méi)有用到線(xiàn)程池畜号,對(duì)這個(gè)概念也不太熟悉帚呼,最近看書(shū)的時(shí)候看到線(xiàn)程池绍移,學(xué)習(xí)了它在Android中的應(yīng)用,來(lái)總結(jié)記錄一下急凰。
線(xiàn)程池:線(xiàn)程池是一個(gè)對(duì)象池(String也有一個(gè)字符串對(duì)象池女仰,當(dāng)我們用"123"這樣的形式聲明一個(gè)字符串對(duì)象時(shí),jvm會(huì)先在String對(duì)象池中找有沒(méi)有這個(gè)字符串對(duì)象抡锈,如果有疾忍,就將這個(gè)字符串對(duì)象的引用直接返回就可以,如果沒(méi)有床三,就在String對(duì)象池中先創(chuàng)建一個(gè)"123"的字符串對(duì)象)一罩,所有對(duì)象池都有一個(gè)共性,就是最大程度的復(fù)用對(duì)象撇簿,省去了重復(fù)創(chuàng)建聂渊,銷(xiāo)毀對(duì)象的時(shí)間開(kāi)銷(xiāo)。而線(xiàn)程池就是管理線(xiàn)程四瘫,最大程度的利用線(xiàn)程汉嗽。當(dāng)有線(xiàn)程任務(wù)時(shí),從池中取一個(gè)線(xiàn)程來(lái)執(zhí)行任務(wù)找蜜,執(zhí)行結(jié)束之后饼暑,線(xiàn)程對(duì)象歸池。
進(jìn)程和線(xiàn)程:關(guān)于進(jìn)程和線(xiàn)程需要提一下洗做,一個(gè)進(jìn)程可以有多個(gè)線(xiàn)程弓叛,每個(gè)進(jìn)程有獨(dú)立的運(yùn)行空間,而所有的線(xiàn)程共享一片內(nèi)存空間诚纸,而每個(gè)線(xiàn)程有獨(dú)立的椬辏空間也存儲(chǔ)線(xiàn)程執(zhí)行過(guò)程中的數(shù)據(jù)。
線(xiàn)程池的優(yōu)點(diǎn):
- 避免頻繁的創(chuàng)建和銷(xiāo)毀線(xiàn)程帶來(lái)的時(shí)間開(kāi)銷(xiāo)畦徘。
- 避免多個(gè)線(xiàn)程之間因搶占系統(tǒng)資源導(dǎo)致的線(xiàn)程阻塞毕籽。
- 能夠?qū)€(xiàn)程進(jìn)行簡(jiǎn)單的管理抬闯,提供間隔執(zhí)行,定時(shí)執(zhí)行等功能影钉。
子線(xiàn)程和主線(xiàn)程
Android的主線(xiàn)程就是UI線(xiàn)程画髓,UI線(xiàn)程負(fù)責(zé)和用戶(hù)進(jìn)行交互掘剪,保持高度的響應(yīng)平委,所以在主線(xiàn)程中不能加入耗時(shí)操作,避免線(xiàn)程不能及時(shí)響應(yīng)用戶(hù)操作夺谁,出現(xiàn)ANR現(xiàn)象廉赔。而在Android3.0之后,所有的網(wǎng)絡(luò)請(qǐng)求都不允許在主線(xiàn)程執(zhí)行匾鸥,也是因?yàn)檫@個(gè)原因蜡塌。所以我們通常會(huì)開(kāi)啟一個(gè)線(xiàn)程來(lái)執(zhí)行耗時(shí)任務(wù)。
1.子線(xiàn)程和主線(xiàn)程交互問(wèn)題
在子線(xiàn)程從進(jìn)行耗時(shí)任務(wù)勿负,在任務(wù)完成之后需要通知用戶(hù)馏艾,這個(gè)時(shí)候就需要更新UI。Android的UI線(xiàn)程是線(xiàn)程不安全的奴愉,如果想要更新UI琅摩,必須在主線(xiàn)程中進(jìn)行。這個(gè)時(shí)候锭硼,我們可以考慮使用Handler房资。
在子線(xiàn)程中發(fā)送消息,由主線(xiàn)程中的Handler接收檀头,并進(jìn)行消息處理轰异。
注:這里只是簡(jiǎn)單演示一下,實(shí)際開(kāi)發(fā)中的Handler不能這樣寫(xiě)暑始,要寫(xiě)成靜態(tài)匿名內(nèi)部類(lèi)+軟引用的方式搭独,否則很容易引起內(nèi)存泄漏,由于此次的重點(diǎn)不在這里廊镜,就不詳細(xì)說(shuō)了牙肝,貼一篇我看過(guò)的文章,這里寫(xiě)的很詳細(xì)期升,感興趣的可以看一下惊奇,Android 內(nèi)存泄露總結(jié) .
//主線(xiàn)程中
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
// 在這里可以進(jìn)行UI操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
//子線(xiàn)程中
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); // 將Message對(duì)象發(fā)送出去
異步消息處理機(jī)制
Android中的異步消息主要由4部分構(gòu)成,Handler播赁,Message颂郎,MessageQueue,Lopper.
Message:在線(xiàn)程之間傳遞的消息容为,內(nèi)部可攜帶少量信息乓序。
Handler:處理和發(fā)送消息寺酪,sendMessage()和handleMessage().
MessageQueue:消息隊(duì)列,用于存放Handler發(fā)送的消息替劈,消息隊(duì)列中的消息等待被處理寄雀,每個(gè)線(xiàn)程只有一個(gè)MessageQueue.
Lopper:線(xiàn)程中的MessageQueue的管家,調(diào)用Lopper的loop()就會(huì)進(jìn)入無(wú)限循環(huán)中陨献,每當(dāng)MessageQueue中有消息盒犹,Looper就會(huì)將它取出,傳遞到Handler的handleMessage()方法中眨业。每個(gè)線(xiàn)程中只有一個(gè)Looper急膀。
經(jīng)過(guò)下面的處理過(guò)程,消息就從子線(xiàn)程回到了主線(xiàn)程中龄捡。
線(xiàn)程池
Java中線(xiàn)程池的頂級(jí)是Excutor卓嫂,而真正的實(shí)現(xiàn)類(lèi)是ExcutorService,ExcutorService的默認(rèn)實(shí)現(xiàn)是ThreadPoolExcutor,可以使用Excutors類(lèi)中的方法自動(dòng)創(chuàng)建線(xiàn)程池聘殖。
Excutors中默認(rèn)實(shí)現(xiàn)的幾種線(xiàn)程:
newCachedThreadPool:返回一個(gè)能根據(jù)實(shí)際情況調(diào)整線(xiàn)程池中線(xiàn)程數(shù)量的線(xiàn)程池晨雳,也就是說(shuō),線(xiàn)程的數(shù)量是不固定的奸腺。調(diào)用 execute() 將重用以前構(gòu)造的線(xiàn)程(如果線(xiàn)程可用)餐禁。如果現(xiàn)有線(xiàn)程沒(méi)有可用的,則創(chuàng)建一個(gè)新線(xiàn)程并添加到池中洋机,去處理新任務(wù)坠宴。終止并從緩存中移除那些已有 60 秒鐘未被使用的線(xiàn)程(60s是該線(xiàn)程的默認(rèn)“保持活動(dòng)時(shí)間”)。因此绷旗,長(zhǎng)時(shí)間保持空閑的線(xiàn)程池不會(huì)使用任何資源喜鼓。注意,可以使用 ThreadPoolExecutor 構(gòu)造方法創(chuàng)建具有類(lèi)似屬性但細(xì)節(jié)不同(例如超時(shí)參數(shù))的線(xiàn)程池衔肢。
newFixedThreadPool:返回一個(gè)有固定線(xiàn)程數(shù)量的線(xiàn)程池庄岖,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線(xiàn)程,直到線(xiàn)程達(dá)到線(xiàn)程池的最大大小角骤,線(xiàn)程池的大小一旦達(dá)到最大值就會(huì)保持不變隅忿,如果某個(gè)線(xiàn)程因?yàn)閳?zhí)行異常而結(jié)束,那么線(xiàn)程池會(huì)補(bǔ)充一個(gè)新線(xiàn)程邦尊。如果有新任務(wù)提交背桐,但是沒(méi)有空閑線(xiàn)程來(lái)處理,線(xiàn)程數(shù)量已經(jīng)達(dá)到了最大值蝉揍,這時(shí)任務(wù)會(huì)被加入任務(wù)隊(duì)列中链峭,等待空閑線(xiàn)程來(lái)執(zhí)行。
newSingleThreadExecutor:創(chuàng)建是一個(gè)單線(xiàn)程池又沾,也就是該線(xiàn)程池只有一個(gè)線(xiàn)程在工作弊仪,所有的任務(wù)是串行執(zhí)行的熙卡,如果這個(gè)唯一的線(xiàn)程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線(xiàn)程來(lái)替代它励饵,此線(xiàn)程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行驳癌。該線(xiàn)程池每次只能只執(zhí)行一個(gè)線(xiàn)程任務(wù),多余的任務(wù)放入任務(wù)隊(duì)列役听,等待順序執(zhí)行颓鲜。
newScheduledThreadPool:創(chuàng)建一個(gè)大小無(wú)限的線(xiàn)程池,此線(xiàn)程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求禾嫉。
ThreadPoolExcutor構(gòu)造函數(shù)灾杰,相關(guān)參數(shù)說(shuō)明:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
1)corePoolSize:線(xiàn)程池的核心線(xiàn)程數(shù)蚊丐,一般情況下不管有沒(méi)有任務(wù)都會(huì)一直在線(xiàn)程池中一直存活熙参,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true 時(shí),閑置的核心線(xiàn)程會(huì)存在超時(shí)機(jī)制麦备,如果在指定時(shí)間沒(méi)有新任務(wù)來(lái)時(shí)孽椰,核心線(xiàn)程也會(huì)被終止,而這個(gè)時(shí)間間隔由第3個(gè)屬性 keepAliveTime 指定凛篙。
2)maximumPoolSize:線(xiàn)程池所能容納的最大線(xiàn)程數(shù)黍匾,當(dāng)活動(dòng)的線(xiàn)程數(shù)達(dá)到這個(gè)值后,后續(xù)的新任務(wù)將會(huì)被阻塞呛梆。
3)keepAliveTime:控制線(xiàn)程閑置時(shí)的超時(shí)時(shí)長(zhǎng)锐涯,超過(guò)則終止該線(xiàn)程。一般情況下用于非核心線(xiàn)程填物,只有在ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true時(shí)纹腌,也作用于核心線(xiàn)程。
4)unit:用于指定 keepAliveTime 參數(shù)的時(shí)間單位滞磺,TimeUnit 是個(gè) enum 枚舉類(lèi)型升薯,常用的有:TimeUnit.HOURS(小時(shí))、TimeUnit.MINUTES(分鐘)击困、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等涎劈。
5)workQueue:線(xiàn)程池的任務(wù)隊(duì)列,通過(guò)線(xiàn)程池的 execute(Runnable command) 方法會(huì)將任務(wù) Runnable 存儲(chǔ)在隊(duì)列中阅茶。
6)threadFactory:線(xiàn)程工廠(chǎng)蛛枚,它是一個(gè)接口,用來(lái)為線(xiàn)程池創(chuàng)建新線(xiàn)程的脸哀。