是一個具備必定獨立功能的程序在一個數據集上的一次動態執行的過程,是操做系統進行資源分配和調度的一個獨立單位,是應用程序運行的載體。java
線程是操做系統可以進行運算調度的最小單位。git
它被包含在進程之中,是進程中的實際運做單位。github
一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務。面試
進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。數組
進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響。安全
線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。網絡
但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。數據結構
單線程程序:程序執行過程當中只有一個有效操做的序列,不一樣操做之間都有明確的執行前後順序,容易出現代碼阻塞多線程
多線程程序:有多個線程,線程間獨立運行,能有效地避免代碼阻塞,而且提升程序的運行性能架構
(1)使用多線程能夠減小程序的響應時間。 在單線程的狀況下,若是某個程序很耗時或者陷入長時間等待(如等待網絡響應),此時程序將不會相應鼠標和鍵盤等操做,使用多線程後,能夠把這個耗時的線程分配到一個單獨的線程去執行,從而是程序具有了更好的交互性。
(2)與進程相比,線程的建立和切換開銷更小。 因爲啓動一個新的線程必須給這個線程分配獨立的地址空間,創建許多數據結構來維護線程代碼段、數據段等信息,而運行於同一個進程內的線程共享代碼段、數據段,線程的啓動或切換的開銷就比進程要少不少。同時多線程在數據共享方面效率很是高。
(3)多CPU或多核心計算機自己就具備執行多線程的能力。 若是使用單個線程,將沒法重複利用計算機資源,形成資源的巨大浪費。所以在多CPU計算機上使用多線程能提升CPU的利用率。
(4)使用多線程能簡化程序的結構,使用程序便於理解和維護。 一個很是複雜的進程能夠分紅多個線程來執行。
當多個線程訪問同一個對象時,若是不用考慮這些線程在運行時環境下的調度和交替運行,也不須要進行額外的同步,或者在調用方進行任何其餘的協調操做,調用這個對象的行爲均可以獲取正確的結果,那這個對象是線程安全的。 ——<<深刻Java虛擬機>>
Java容許多線程併發控制,當多個線程同時操做一個可共享的資源變量時(如數據的增刪改查),將會致使數據不許確,相互之間產生衝突。
所以加入同步鎖以免在該線程沒有完成操做以前,被其餘線程的調用,從而保證了該變量的惟一性和準確性。
檢查數值、改變數值,以及可能發生的睡眠操做均做爲單一的、不可分割的原子操做完成。
二、進程間的內部數據和狀態都是相互徹底獨立的,所以進程間通訊大多數狀況是必須經過網絡實現。線程自己的數據,一般只有寄存器數據,以及一個程序執行時使用的堆棧,因此線程的切換比進程切換的負擔要小。
三、CPU對於各個線程的調度是隨機的(分時調度),在Java程序中,JVM負責線程的調度。 線程調度是指按照特定的機制爲多個線程分配CPU的使用權,也就是實際執行的時候是線程,所以CPU調度的最小單位是線程,而資源分配的最小單位是進程。
棧:在函數中定義的基本類型的變量和對象的引用變量都是在函數的棧內存中分配。
堆:堆內存用於存放由new建立的對象和數組。
從通俗化的角度來講,堆是用來存放對象的,棧是用來存放執行程序的
-Xss參數用來控制線程的堆棧大小。
當兩個線程競爭同一資源時,若是對資源的訪問順序敏感,就稱存在競態條件。
在臨界區中使用適當的同步就能夠避免競態條件。
界區實現方法有兩種,一種是用synchronized,一種是用Lock顯式鎖實現。
不可變的對象必定是線程安全的,而且永遠也不須要額外的同步。
Java類庫中大多數基本數值類如Integer、String和BigInteger都是不可變的。
由類的規格說明所規定的約束在對象被多個線程訪問時仍然有效,無論運行時環境如何排列,線程都不須要任何額外的同步。
如 Random 、ConcurrentHashMap、Concurrent集合、atomic
有條件的線程安全類對於單獨的操做能夠是線程安全的,可是某些操做序列可能須要外部同步。
有條件線程安全的最多見的例子是遍歷由 Hashtable 或者 Vector 或者返回的迭代器
線程兼容類不是線程安全的,可是能夠經過正確使用同步而在併發環境中安全地使用。
如ArrayList HashMap
線程對立是那些無論是否採用了同步措施,都不能在多線程環境中併發使用的代碼。
如如System.setOut()、System.runFinalizersOnExit()
每個線程都是有優先級的,通常來講,高優先級的線程在運行時會具備優先權,但這依賴於線程調度的實現,這個實現是和操做系統相關的(OSdependent)。
能夠定義線程的優先級,可是這並不能保證高優先級的線程會在低優先級的線程前執行。線程優先級是一個int變量(從1-10),1表明最低優先級,10表明最高優先級。
線程調度器是一個操做系統服務,它負責爲Runnable狀態的線程分配CPU時間。一旦建立一個線程並啓動它,它的執行便依賴於線程調度器的實現。
時間分片是指將可用的CPU時間分配給可用的Runnable線程的過程。分配CPU時間能夠基於線程優先級或者線程等待的時間。
線程調度並不受到Java虛擬機控制,因此由應用程序來控制它是更好的選擇。
單核CPU也支持多線程執行代碼,CPU經過給每一個線程分配CPU時間片來實現這個機制。時間片是CPU分配給各個線程的時間,由於時間片很是短,因此CPU經過不停地切換線程執行,讓咱們感受多個線程時同時執行的,時間片通常是幾十毫秒(ms)。
操做系統中,CPU時間分片切換到另外一個就緒的線程,則須要保存當前線程的運行的位置,同時須要加載須要恢復線程的環境信息。
守護線程都是爲JVM中全部非守護線程的運行提供便利服務: 只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就所有工做;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工做。
User和Daemon二者幾乎沒有區別,惟一的不一樣之處就在於虛擬機的離開:若是 User Thread已經所有退出運行了,只剩下Daemon Thread存在了,虛擬機也就退出了。
由於沒有了被守護者,Daemon也就沒有工做可作了,也就沒有繼續運行程序的必要了。
任何線程均可以設置爲守護線程和用戶線程,經過方法Thread.setDaemon(bool on);true則把該線程設置爲守護線程,反之則爲用戶線程。Thread.setDaemon()必須在Thread.start()以前調用,不然運行時會拋出異常。
守護線程至關於後臺管理者 好比 : 進行內存回收,垃圾清理等工做
一、新建狀態(New):新建立了一個線程對象。
二、就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。
三、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
四、阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的狀況分三種: (一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖) (二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。 (三)、其餘阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖)
五、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
Java提供的終止方法只有一個stop,可是不建議使用此方法,由於它有如下三個問題:
原文:Java架構筆記
免費Java高級資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分佈式等教程,一共30G。
傳送門: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q