摘要:多線程編程是現(xiàn)代軟件技術(shù)中很重要的一個(gè)環(huán)節(jié)。要弄懂多線程,這就要牽涉到多進(jìn)程。本文主要以多線程編程以及多線程編程相關(guān)知識(shí)而做出的一些結(jié)論。
什么是多線程編程?
多線程編程技術(shù)是Java語(yǔ)言的重要特點(diǎn)。多線程編程的含義是將程序任務(wù)分成幾個(gè)并行的子任務(wù)。特別是在網(wǎng)絡(luò)編程中,你會(huì)發(fā)現(xiàn)很多功能是可以并發(fā)執(zhí)行的。比如網(wǎng)絡(luò)傳輸速度較慢、用戶輸入速度較慢,你可以用兩個(gè)獨(dú)立的線程去完成這兩個(gè)功能,而不影響正常的顯示或其它功能。
多線程是與單線程比較而言的,普通的Windows采用單線程程序結(jié)構(gòu),其工作原理是:主程序有一個(gè)消息循環(huán),不斷從消息隊(duì)列中讀入消息來決定下一步所要干的事情,一般是針對(duì)一個(gè)函數(shù),只有等這個(gè)函數(shù)執(zhí)行完之后,主程序才能接收另外的消息來執(zhí)行。比如子函數(shù)功能是在讀一個(gè)網(wǎng)絡(luò)數(shù)據(jù),或讀一個(gè)文件,只有等讀完這個(gè)數(shù)據(jù)或文件才能接收下一個(gè)消息。在執(zhí)行這個(gè)子函數(shù)過程中你什么也不能干。但往往讀網(wǎng)絡(luò)數(shù)據(jù)和等待用戶輸入有很多時(shí)間處于等待狀態(tài),多線程利用這個(gè)特點(diǎn)將任務(wù)分成多個(gè)并發(fā)任務(wù)后,就可以解決這個(gè)問題。
多線程編程基礎(chǔ)知識(shí)詳解
一、進(jìn)程
1.程序:由源代碼生成的可執(zhí)行應(yīng)用。(如:QQ.app)
2.進(jìn)程:一個(gè)正在運(yùn)行的程序可以看做一個(gè)進(jìn)程。(正在運(yùn)行的QQ就是一個(gè)進(jìn)程),進(jìn)程擁有獨(dú)立運(yùn)行所需的全部資源。
3.線程:程序中獨(dú)立運(yùn)行的代碼段。(如:接收QQ消息的代碼塊)
一個(gè)進(jìn)程是由一個(gè)和多個(gè)線程組成,進(jìn)程只負(fù)責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元,負(fù)責(zé)代碼的執(zhí)行。
二、單線程
1、每個(gè)正在運(yùn)行的程序(即進(jìn)程),至少包含一個(gè)線程,這個(gè)線程叫主線程。
2、主線程在程序啟動(dòng)時(shí)被創(chuàng)建,用于執(zhí)行main函數(shù)。
3、只有一個(gè)主線程的程序,稱作單線程程序。
4、主線程負(fù)責(zé)執(zhí)行程序所有的代碼(UI展現(xiàn)以及刷新,網(wǎng)絡(luò)請(qǐng)求,本地請(qǐng)求)。這些代碼只能順序執(zhí)行,無(wú)法并發(fā)執(zhí)行。
注意:UI展現(xiàn)和刷新只能寫在主線程中。
三、多線程
1、擁有多個(gè)線程的程序,稱作多線程程序。
2、iOS允許用戶自己開辟新的線程,相對(duì)于主線程來說,這些線程,稱作子線程。
3、子線程和主線程都是獨(dú)立運(yùn)行的單元,各自的執(zhí)行互不影響,因此能夠并發(fā)執(zhí)行。
補(bǔ)充的多線程的基本知識(shí)
4、iOS默認(rèn)給主線程分配1M的棧空間,默認(rèn)給子線程分配512K的棧空間。(分配的字節(jié)數(shù)必須是4K的整數(shù)倍)
5、分配的空間用來存放線程中為變量開辟的空間,一般情況下足夠使用。
6、與棧空間的使用方式不同,主線程和子線程共用同一塊內(nèi)存空間。
7、程序入口處默認(rèn)設(shè)置了自動(dòng)釋放池,由主線程負(fù)責(zé)執(zhí)行代碼。子線程新開辟的內(nèi)存不在主線程管轄范圍內(nèi)(線程之間互不干擾,相互獨(dú)立),所以子線程為對(duì)象開辟的空間不會(huì)自動(dòng)釋放,要手動(dòng)為子線程添加自動(dòng)釋放池。
8、短時(shí)間內(nèi)使用靜態(tài)方法開辟大量?jī)?nèi)存空間或使用循環(huán)使用便利構(gòu)造器,會(huì)造成內(nèi)存瞬間集聚,程序carsh掉,所以要寫@autorelease pool。
9、多線程的種類
脫離線程:線程結(jié)束后被銷毀,子線程可能是脫離線程。
四、單、多線程的區(qū)別
單線程程序:只有一個(gè)線程,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁(yè)面假死)
多線程程序:有多個(gè)線程,線程之間獨(dú)立運(yùn)行,能有效的避免代碼阻塞,并且提高程序的運(yùn)行性能。
五、JVM與多線程
Java編寫的程序都運(yùn)行在Java虛擬機(jī)(JVM)中,在JVM的內(nèi)部,程序的多任務(wù)是通過線程來實(shí)現(xiàn)的。
每用java命令啟動(dòng)一個(gè)java應(yīng)用程序,就會(huì)啟動(dòng)一個(gè)JVM進(jìn)程。在同一個(gè)JVM進(jìn)程中,有且只有一個(gè)進(jìn)程,就是它自己。在這個(gè)JVM環(huán)境中,所有程 序代碼的運(yùn)行都是以線程來運(yùn)行的。JVM找到程序程序的入口點(diǎn)main(),然后運(yùn)行main()方法,這樣就產(chǎn)生了一個(gè)線程,這個(gè)線程稱之為主線程。當(dāng) main方法結(jié)束后,主線程運(yùn)行完成。JVM進(jìn)程也隨即退出。
操作系統(tǒng)將進(jìn)程線程進(jìn)行管理,輪流(沒有固定的順序)分配每個(gè)進(jìn)程很短的一段時(shí)間(不一定是均分),然后在每個(gè)進(jìn)程內(nèi)部,程序代碼自己處理該進(jìn)程內(nèi)部線程的時(shí)間分配,多個(gè)線程之間相互的切換去執(zhí)行,這個(gè)切換時(shí)間也是非常短的。
六、Java語(yǔ)言對(duì)多線程的支持
Java語(yǔ)言對(duì)多線程的支持通過類Thread和接口Runnable來實(shí)現(xiàn)。這里就不多說了。這里重點(diǎn)強(qiáng)調(diào)兩個(gè)地方:
// 主線程其它代碼段
ThreadClass subThread = new ThreadClass(); subThread.start(); // 主線程其它代碼段 subThread.sleep(1000);
非脫離線程:線程結(jié)束后被掛起,等待喚醒,不銷毀。主線程一定是非脫離線程。
10、iOS中實(shí)現(xiàn)多線程的方法
a、NSObject
b、NSThread
c、NSOperation和NSOperationQueue結(jié)合使用
d、GCD(最重要的)
有人認(rèn)為以下的代碼在調(diào)用start()方法后,肯定是先啟動(dòng)子線程,然后主線程繼續(xù)執(zhí)行。在調(diào)用sleep()方法后CPU什么都不做,就在那里等待休眠的時(shí)間結(jié)束。實(shí)際上這種理解是錯(cuò)誤的。因?yàn)椋?/p>
①start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\(yùn)行態(tài)(Runnable),什么時(shí)候運(yùn)行是由操作系統(tǒng)決定的。
②Thread.sleep()方法調(diào)用目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲取的CPU資源,以留出一定時(shí)間給其他線程執(zhí)行的機(jī)會(huì)(也就是靠?jī)?nèi)部自己協(xié)調(diào))。
七、線程的狀態(tài)切換
1、新建狀態(tài)(New):新創(chuàng)建了一個(gè)線程對(duì)象。
2、就緒狀態(tài)(Runnable):線程對(duì)象創(chuàng)建后,其他線程調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,變得可運(yùn)行,等待獲取CPU的使用權(quán)。
3、運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
4、阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
①等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,JVM會(huì)把該線程放入等待池中。 ②同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM把該線程放入鎖池③其他阻塞:運(yùn)行的線程執(zhí)行sleep()或 join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處 理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。
5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
八、為什么要多線程編程
為什么要多線程編程呢?這其中的原因很多,我們可以舉例解決
1)有的是為了提高運(yùn)行的速度,比如多核cpu下的多線程
2)有的是為了提高資源的利用率,比如在網(wǎng)絡(luò)環(huán)境下下載資源時(shí),時(shí)延常常很高,我們可以通過不同的thread從不同的地方獲取資源,這樣可以提高效率
3)有的為了提供更好的服務(wù),比如說是服務(wù)器
4)其他需要多線程編程的地方等等
九、Java的線程模型
由于Java是純面向?qū)ο笳Z(yǔ)言,因此,Java的線程模型也是面向?qū)ο蟮摹ava通過Thread類將線程所必須的功能都封裝了起來。要想建立一個(gè)線 程,必須要有一個(gè)線程執(zhí)行函數(shù),這個(gè)線程執(zhí)行函數(shù)對(duì)應(yīng)Thread類的run方法。Thread類還有一個(gè)start方法,這個(gè)方法負(fù)責(zé)建立線程,相當(dāng)于 調(diào)用Windows的 建立線程函數(shù)CreateThread.當(dāng)調(diào)用start方法后,如果線程建立成功,并自動(dòng)調(diào)用Thread類的run方法。因此,任何繼承Thread 的Java類都可以通過Thread類的start方法來建立線程。如果想運(yùn)行自己的線程執(zhí)行函數(shù),那就要覆蓋Thread類的run方法。
在Java的線程模型中除了Thread類,還有一個(gè)標(biāo)識(shí)某個(gè)Java類是否可作為線程類的接口Runnable,這個(gè)接口只有一個(gè)抽象方法run,也就 是Java線程模型的線程執(zhí)行函數(shù)。因此,一個(gè)線程類的唯一標(biāo)準(zhǔn)就是這個(gè)類是否實(shí)現(xiàn)了Runnable接口的run方法,也就是說,擁有線程執(zhí)行函數(shù)的類 就是線程類。 從上面可以看出,在Java中建立線程有兩種方法,一種是繼承Thread類,另一種是實(shí)現(xiàn)Runnable接口,并通過Thread和實(shí)現(xiàn) Runnable的類來建立線程,其實(shí)這兩種方法從本質(zhì)上說是一種方法,即都是通過Thread類來建立線程,并運(yùn)行run方法的。但它們的大區(qū)別是通過 繼承Thread類來建立線程,雖然在實(shí)現(xiàn)起來更容易,但由于Java不支持多繼承,因此,這個(gè)線程類如果繼承了Thread,就不能再繼承其他的類了, 因此,Java線程模型提供了通過實(shí)現(xiàn)Runnable接口的方法來建立線程,這樣線程類可以在必要的時(shí)候繼承和業(yè)務(wù)有關(guān)的類,而不是Thread類。
評(píng)論