線程:是系統(tǒng)進(jìn)行運(yùn)算調(diào)度的最小單位
進(jìn)程:是線程的容器喉刘,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,一個(gè)進(jìn)程可以并發(fā)多個(gè)線程
在Android中,系統(tǒng)啟動(dòng)時(shí)默認(rèn)創(chuàng)建主線程,但是主線程(UI線程)不能進(jìn)行耗時(shí)操作懈玻,否則android程序會(huì)無(wú)響應(yīng),所以當(dāng)有耗時(shí)等操作的時(shí)候就可以創(chuàng)建一個(gè)子線程乾颁。
使用線程的優(yōu)點(diǎn):
1.資源利用率更高
例如從磁盤讀取文件的時(shí)候涂乌,讀取一個(gè)文件5s艺栈,處理一個(gè)文件2s,當(dāng)需要讀取2個(gè)文件骂倘,但只有一個(gè)單線程的時(shí)候眼滤,他就會(huì)順序去執(zhí)行,先讀取A并處理完A文件的時(shí)候历涝,才回去讀取B诅需,這樣花費(fèi)的時(shí)間需要14s,但是CPU時(shí)間還有很多空閑荧库。當(dāng)使用兩個(gè)線程時(shí)堰塌,就能讀取完A后,同時(shí)開(kāi)始處理A和讀取B文件的操作分衫,這時(shí)候CPU時(shí)間使用更多场刑,最終花費(fèi)時(shí)間只需要12s
2.程序響應(yīng)更快
例如一個(gè)人服務(wù)器程序是單線程的,此時(shí)如果一個(gè)請(qǐng)求需要占用大量的時(shí)間在這段時(shí)間內(nèi)新的客戶端就無(wú)法發(fā)送請(qǐng)求給服務(wù)器端蚪战,如果引入多線程則會(huì)大大提高效率牵现。
從一個(gè)單線程的應(yīng)用到多線程的應(yīng)用并不僅僅帶來(lái)好處,其也會(huì)帶來(lái)一些代價(jià)邀桑,不要僅僅為了使用多線程而使用多線程瞎疼,應(yīng)該明確在使用多線程的時(shí)候能帶來(lái)的好處比付出的代價(jià)大的時(shí)候,才使用多線程壁畸。
但是有優(yōu)點(diǎn)贼急,當(dāng)然也有缺點(diǎn),現(xiàn)在介紹一下缺點(diǎn)捏萍。
使用線程的缺點(diǎn):
1.等候共享資源會(huì)變慢:
當(dāng)兩個(gè)線程同時(shí)需要使用一個(gè)資源的時(shí)候太抓,就會(huì)出現(xiàn)占用,降低運(yùn)行時(shí)間
2.對(duì)線程管理產(chǎn)生額外的開(kāi)銷:
程的使用會(huì)給系統(tǒng)帶來(lái)上下文切換的額外負(fù)擔(dān)令杈。當(dāng)這種負(fù)擔(dān)超過(guò)一定程度時(shí),多線程的特點(diǎn)主要表現(xiàn)在其缺點(diǎn)上,比如用獨(dú)立的線程來(lái)更新數(shù)組內(nèi)每個(gè)元素走敌。
3.線程的死鎖。即較長(zhǎng)時(shí)間的等待或資源競(jìng)爭(zhēng)以及死鎖等多線程癥狀逗噩。
4.對(duì)公有變量的同時(shí)讀或?qū)懙衾觥.?dāng)多個(gè)線程需要對(duì)公有變量進(jìn)行寫操作時(shí),后一個(gè)線程往往會(huì)修改掉前一個(gè)線程存放的數(shù)據(jù),從而使前一個(gè)線程的參數(shù)被修改;另外 ,當(dāng)公用變量的讀寫操作是非原子性時(shí),在不同的機(jī)器上,中斷時(shí)間的不確定性,會(huì)導(dǎo)致數(shù)據(jù)在一個(gè)線程內(nèi)的操作產(chǎn)生錯(cuò)誤,從而產(chǎn)生莫名其妙的錯(cuò)誤,而這種錯(cuò)誤是程序員無(wú)法預(yù)知的。
當(dāng)然以上的缺點(diǎn)只要涉及合理都是可以避免的
但是設(shè)計(jì)開(kāi)銷會(huì)增加就無(wú)法避免给赞。
android四種常用的操作多線程的方式
1机打、Handle機(jī)制(最常用)
Handle 的一些常用參數(shù)解釋:
Message:消息,就是一個(gè)載體片迅,包含消息ID残邀,消息處理對(duì)象和處理的數(shù)據(jù)等(msg.what ,msg.arg ,msg.obj等).
Handler:用于同一個(gè)進(jìn)程的線程間通信,消息處理者,專門負(fù)責(zé)Message的發(fā)送和處理芥挣。我們使用Handler時(shí)驱闷,一般通過(guò)handleMessage(Message msg)來(lái)處理Message,也就是統(tǒng)一處理消息的回調(diào)空免,確保自己發(fā)出的消息也是自己來(lái)處理空另。
MessageQueue:隊(duì)列,就是存放Handler發(fā)送過(guò)來(lái)的消息蹋砚,按照先進(jìn)先出的順序規(guī)則來(lái)執(zhí)行扼菠。將鏈表的數(shù)據(jù)結(jié)構(gòu)以Message來(lái)串聯(lián)起來(lái),等待Looper的抽取MessageQueue,其他非主線程需要looper的時(shí)候就會(huì)通過(guò)調(diào)用prepare函數(shù)來(lái)實(shí)現(xiàn)坝咐。
Looper:首先要理解一個(gè)線程是一段可執(zhí)行的代碼循榆,作為App的主線程,不能讓代碼執(zhí)行完墨坚,因?yàn)榇a執(zhí)行完的話app就會(huì)自動(dòng)退出秧饮,因此不能讓主線程不能讓代碼段執(zhí)行,只能在代碼中插入一個(gè)死循環(huán)泽篮,這時(shí)候Looper的作用就體現(xiàn)出來(lái)了盗尸,將主線程變成Looper線程。并且這時(shí)主線程就會(huì)在等其他線程發(fā)消息(更新UI和Activity狀態(tài)等等)那帽撑,另外泼各,Looper不斷從消息隊(duì)列拿出消息給主線程,也就是無(wú)限循環(huán)去查找是否有消息油狂,有就去處理历恐,沒(méi)有的話就一直等待寸癌,一個(gè)MessageQueue需要一個(gè)Looper专筷。
Thread:負(fù)責(zé)調(diào)度整個(gè)消息的循環(huán)
怎么利用Handle,讓主線程與子線程通信蒸苇?
答:可以利用HandlerThread進(jìn)行生成一個(gè)子線程的Handler磷蛹,并且實(shí)現(xiàn)handlerMessage方法,然后在主線程里面也生成一個(gè)Handler溪烤,然后通過(guò)調(diào)用sendMessage方法進(jìn)行通知子線程味咳。同樣,子線程里面也可以調(diào)用sendMessage方法進(jìn)行通知主線程檬嘀。這樣做的好處比如有些圖片的加載啊槽驶,網(wǎng)絡(luò)的訪問(wèn)啊可能會(huì)比較耗時(shí),所以放到子線程里面做是比較合適的
Handle 實(shí)例
1.創(chuàng)建一個(gè)線程
創(chuàng)建一個(gè)子進(jìn)程鸳兽,并讓其休眠掂铐,模擬執(zhí)行一個(gè)耗時(shí)操作
public class HandleTest extends Thread{
? ? //Thread build
? ? private String name; //Thread name
? ? public HandleTest(String name){
? ? ? ? this.name=name;
? ? }
? ? static int i = 5;
? ? public void run(){
? ? ? ? Message msg = new Message();
? ? ? ? try {
? ? ? ? ? ? //sleep set 8000
? ? ? ? ? ? i = 3;
? ? ? ? ? ? sleep(8000);
? ? ? ? ? ? msg.what = 1;
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
在里面創(chuàng)建一個(gè)整型常量3,當(dāng)結(jié)束的時(shí)候傳出參數(shù),并輸出1333全陨,但是在子線程沒(méi)辦法傳回參數(shù)3爆班,因?yàn)閞un是一個(gè)void返回值,這時(shí)候怎么辦辱姨?
public class HandleTest extends Thread{
? //Thread build
? private String name; //Thread name
? public HandleTest(String name){
? ? ? this.name=name;
? }
? static int i = 5;
? public void run(){
? ? ? //instantiation message
? ? ? Message msg = new Message();
? ? ? try {
? ? ? ? ? //sleep set 8000
? ? ? ? ? i = 3;
? ? ? ? ? sleep(8000);
? ? ? // send messge.what = 1 to handle.message
? ? ? ? ? msg.what = 1;
? ? ? } catch (InterruptedException e) {
? ? ? ? ? e.printStackTrace();
? ? ? }
? }
}
有兩種解決辦法柿菩,判斷msg.what或回調(diào)msg數(shù)據(jù)
2.實(shí)例化handle類,并建立message雨涛,傳遞參數(shù)
在主線程創(chuàng)建一個(gè)handle
static int i = 1;
public void handleMessage(){
? ? //When receiving the msg.what == 1,i = 3;
? ? switch (msg.what) {
? ? ? ? ? ? case 0:
? ? ? ? ? ? ? ? i = 3;
? ? ? ? ? ? ? ? break;
? ? ? ? }
}
通過(guò)判斷回調(diào)msg.what修改參數(shù)枢舶,同時(shí)也可以用msg.args回調(diào)整型參數(shù)