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