瘋狂Java學習筆記(69)---------Lock">瘋狂Java學習筆記(69)---------Lock
691
2025-03-31
瘋狂Java學習筆記(62)------------線程初識
線程初識
沒有線程編程的程序好比一個人一只手干活,而多線程的程序就好比一個人多只手干活.
進程是系統資源分配的獨立單位,而線程是可調度運行的獨立單位,一個進程可以擁有多個線程,線程是進程并行完成的多個任務.
進程、線程、并發執行
首先我們先來認識一下進程、線程、并發執行的概念:
一般來說,當運行一個應用程序的時候,就啟動了一個進程,當然有些會啟動多個進程。啟動進程的時候,操作系統會為進程分配資源,其中最主要的資源是內存空間,因為程序是在內存中運行的。
在進程中,有些程序流程塊是可以亂序執行的,并且這個代碼塊可以同時被多次執行。實際上,這樣的代碼塊就是線程體。線程是進程中亂序執行的代碼流程。當多個線程同時運行的時候,這樣的執行模式成為并發執行。
下面我以一個日常生活中簡單的例子來說明進程和線程之間的區別和聯系:
這副圖是一個雙向多車道的道路圖,假如我們把整條道路看成是一個“進程”的話,那么圖中由白色虛線分隔開來的各個車道就是進程中的各個“線程”了。
這些線程(車道)共享了進程(道路)的公共資源(土地資源)。
這些線程(車道)必須依賴于進程(道路),也就是說,線程不能脫離于進程而存在(就像離開了道路,車道也就沒有意義了)。
這些線程(車道)之間可以并發執行(各個車道你走你的,我走我的),也可以互相同步(某些車道在交通燈亮時禁止繼續前行或轉彎,必須等待其它車道的車輛通行完畢)。
這些線程(車道)之間依靠代碼邏輯(交通燈)來控制運行,一旦代碼邏輯控制有誤(死鎖,多個線程同時競爭唯一資源),那么線程將陷入混亂,無序之中。
這些線程(車道)之間誰先運行是未知的,只有在線程剛好被分配到CPU時間片(交通燈變化)的那一刻才能知道。
并發和并行的區別就是一個處理器同時處理多個任務和多個處理器或者是多核的處理器同時處理多個不同的任務.
并發是邏輯上的同時發生(simultaneous),并行者是物理上的同時發生.
并發性?(concurrency),又稱共行性,是指能處理多個同時性活動的能力,并發事件之間不一定要同一時刻發生.
并行性?(parallelism) 是指同時發生的兩個并發事件,具有并發的含義,而并發則不一定并行.
也就是說,多進程之間并發執行,而多線程編程就是并行執行的了.
來個比喻:并發和并行的區別就是一個人同時吃三個饅頭和三個人同時吃三個饅頭.
這樣,對于進程與線程,并發和并行的區別,您應該了解了吧.反正我是明白了!
Java中的線程
在Java中,“線程”指 兩件不同的事情:
1、java.lang.Thread類的一個實例;
2、線程的執行。
使用java.lang.Thread類或者java.lang.Runnable接口編寫代碼來定義、實例化和啟動新線程。
一個Thread類實例只是一個對象,像Java中的任何其他對象一樣,具有變量和方法,生死于堆上。
Java中,每個線程都有一個調用棧,即使不在程序中創建任何新的線程,線程也在后臺運行著。
一個Java應用總是從main()方法開始運行,mian()方法運行在一個線程內,它被稱為主線程。
一旦創建一個新的線程,就產生一個新的調用棧。
線程總體分兩類:用戶線程和守候線程。
當所有用戶線程執行完畢的時候,JVM自動關閉。但是守候線程卻不獨立于JVM,守候線程一般是由操作系統或者用戶自己創建的。(此處仍有疑問!)
JVM與多線程
Java編寫的程序都運行在Java虛擬機(JVM)中,在JVM的內部,程序的多任務是通過線程來實現的。
每用java命令啟動一個java應用程序,就會啟動一個JVM進程。在同一個JVM進程中,有且只有一個進程,就是它自己。在這個JVM環境中,所有程序代碼的運行都是以線程來運行的。JVM找到程序的入口點main(),然后運行main()方法,這樣就產生了一個線程,這個線程稱之為主線程。當main方法結束后,主線程運行完成。JVM進程也隨即退出。
操作系統將進程線程進行管理,輪流(沒有固定的順序)分配每個進程很短的一段時間(不一定是均分),然后在每個進程內部,程序代碼自己處理該進程內部線程的時間分配,多個線程之間相互的切換去執行,這個切換時間也是非常短的。
Java語言對多線程的支持
Java語言對多線程的支持通過類Thread和接口Runnable來實現。這里就不多說了。這里重點強調兩個地方:
有人認為以下的代碼在調用start()方法后,肯定是先啟動子線程,然后主線程繼續執行。在調用sleep()方法后CPU什么都不做,就在那里等待休眠的時間結束。實際上這種理解是錯誤的。因為:
start()方法的調用后并不是立即執行多線程代碼,而是使得該線程變為可運行態(Runnable),什么時候運行是由操作系統決定的。
Thread.sleep()方法調用目的是不讓當前線程獨自霸占該進程所獲取的CPU資源,以留出一定時間給其他線程執行的機會(也就是靠內部自己協調)。
線程的狀態切換
前面我們提到,由于線程何時執行是未知的,只有在CPU為線程分配到時間片時,線程才能真正執行。在線程執行的過程中,由可能會因為各種各樣的原因而暫停(就像前面所舉的例子一樣:汽車只有在交通燈變綠的時候才能夠通行,而且在行駛的過程中可能會出現塞車,等待其它車輛通行或轉彎的狀況)。
這樣線程就有了“狀態”的概念,下面這副圖很好的反映了線程在不同情況下的狀態變化。
新建狀態(New):新創建了一個線程對象。
就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位于可運行線程池中,變得可運行,等待獲取CPU的使用權。
運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM把該線程放入鎖。
其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
Java中線程的調度API
Java中關于線程調度的API最主要的有下面幾個:
線程睡眠:Thread.sleep(long millis)方法
線程等待:Object類中的wait()方法
線程讓步:Thread.yield() 方法
線程加入:join()方法
線程喚醒:Object類中的notify()方法
關于這幾個方法的詳細應用,可以參考SUN的API。這里我重點總結一下這幾個方法的區別和使用。
sleep方法與wait方法的區別:
sleep方法是靜態方法,wait方法是非靜態方法。
sleep方法在時間到后會自己“醒來”,但wait不能,必須由其它線程通過notify(All)方法讓它“醒來”。
sleep方法通常用在不需要等待資源情況下的阻塞,像等待線程、數據庫連接的情況一般用wait。
sleep/wait與yeld方法的區別:調用sleep或wait方法后,線程即進入block狀態,而調用yeld方法后,線程進入runnable狀態。
wait與join方法的區別:
wait方法體現了線程之間的互斥關系,而join方法體現了線程之間的同步關系。
wait方法必須由其它線程來解鎖,而join方法不需要,只要被等待線程執行完畢,當前線程自動變為就緒。
join方法的一個用途就是讓子線程在完成業務邏輯執行之前,主線程一直等待直到所有子線程執行完畢。
通過上面的介紹相信同學們對java里面的多線程已經有了基本的了解和認識。其實多線程編程并沒有大家想象中的那么難,只要在實際的學習,工作當中不斷的加以練習和使用,相信大家很快就能掌握其中的奧妙,從而編寫出賞心悅目的java程序。
本文借鑒資料:
http://express.ruanko.com/ruanko-express_6/webpage/tech4.html
http://lavasoft.blog.51cto.com/62575/27069/
轉載自:https://blog.csdn.net/u011225629/article/details/46288665
Java 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。