併發處理的普遍應用是使得amdahl定律代替摩爾定律成爲計算機性能發展源動力的根本緣由,是人類壓榨計算機運算能力的最有力武器。java
併發並不是必定得用多線程,多進程也能夠,不過java裏面談論併發,大多數與線程脫不開關係。所以咱們從線程提及。本文主要包含如下內容:linux
線程的實現windows
線程的調度多線程
線程狀態切換併發
線程是比進程更輕量級的調度執行單位,在linux裏面,線程和進程沒有什麼區別,惟一的就是在地址空間,線程的切換虛擬內存空間依然是相同的,可是進程切換是不一樣的。函數
目前主流的操做系統都提供的線程實現,java則提供的線程實現方法都是native的,由於不一樣的硬件和操做系統提供線程調度方式並不盡相同,因此java沒用採用和平臺無關的統一手段來實現。性能
實現線程的主要3種方式:使用內核線程實現,使用用戶線程實現,使用用戶線程加輕量級進程混合實現。優化
內核線程(KLT)就是直接由操做系統內核支持的線程,這種線程由內核來完成線程切換。lua
程序通常不會直接使用內核線程,而是去使用內核線程的一種高級接口—輕量級進程(LWP),輕量級進程就是咱們所講的線程,這種輕量級進程與內核線程之間1:1的對應關係。spa
優勢:
內核直接支持,由操做系統內核建立和撤銷。內核維護進程及線程的上下文信息以及線程切換。一個內核線程因爲I/O操做而阻塞,不會影響其它線程的運行。
缺點:
一、線程的操做、建立、同步等都須要系統調用,而系統調用代價比較高,須要在用戶態和內核態中來回切換。
二、每一個輕量級的進程都須要一個內核線程來支持,須要消耗必定的內核資源。
用戶線程指不須要內核支持而在用戶程序中實現的線程,其不依賴於操做系統核心,應用進程利用線程庫提供建立、同步、調度和管理線程的函數來控制用戶線程。
不須要用戶態/核心態切換,速度快,操做系統內核不知道多線程的存在,所以一個線程阻塞將使得整個進程(包括它的全部線程)阻塞。使用用戶線程實現的程序通常都比較複雜,java曾經用過,不過最後仍是放棄了。
優勢:
切換由用戶態程序本身控制內核切換,不須要內核干涉,少了進出內核態的消耗。
缺點:
多核處理器很難講線程映射到其餘處理器上,單線程阻塞會形成該進程阻塞。
這種混合模式下,既存在用戶線程,也存在輕量級進程。用戶線程仍是徹底創建在用戶空間中,所以用戶線程的建立、切換、析構等依然廉價,能夠支持大規模的用戶線程併發。
操做系統提供支持的輕量進程做爲用戶線程和內核線程之間的橋樑,用戶線程的系統調用要經過輕量級線程來完成,大大下降了進程阻塞的風險。用戶線程和輕量級進程比是N:M多對對的關係。
java在jdk 1.2以前基於用戶線程實現,在1.2以後,基於操做系統的原生線程模型來實現,在每一個平臺上都不盡相同,好比在windows和linux下都是採用一對一的線程模型實現,在Solaris平臺,採用都是一對一或者多對多來實現(solaris 同時支持一對一和多對多)。
線程調度主要是指系統爲線程分配處理器使用權的過程,主要分爲:協同式線程調度和搶佔式線程調度。
協同式調度中線程的執行時間由線程自己來控制,線程把本身的工做執行完成之後,主動通知系統切換到另外一個線程上。像lua的「協同歷程」就是如此實現的。
優勢:
實現簡單,線程把本身的事情幹完後進行線程切換,切換操做對線程本身是可知的。無同步問題
缺點:
線程執行時間不可控制,若是某個線程出現問題阻塞,會形成程序阻塞。
搶佔式線程調度中每一個線程由系統來分配執行時間,線程的切換不禁線程自己來決定。
優勢:
線程的執行時間系統可控,不會出現單個線程阻塞形成整個進程阻塞。
java就是採用搶佔式線程調度,另外,java還能夠經過給線程設置優先級來建議系統給某些線程多分配一點時間,不過不是很靠譜,線程的調度最終仍是取決的操做系統。
java定義了5中線程狀態,任意一個時間點,一個線程有且只有其中一個狀態。切換以下圖:
(圖片來之:https://my.oschina.net/mingdongcheng/blog/139263)
經過上面咱們知道,java的線程是映射到操做系統的內核線程之上的,若是阻塞或者喚醒一個線程,都是須要操做系統來幫忙完成,這就須要從用戶態轉換到核心態中,因策狀態轉換這一步會耗費不少的處理器時間,須要謹慎使用,具體如何謹慎使用,是否有優化的餘地,咱們下篇再講解。
下一篇咱們聊聊線程的內存模型。
-----------------------------------------------------------------------------
想看更多有趣原創的技術文章,掃描關注公衆號。
關注我的成長和遊戲研發,推進國內遊戲社區的成長與進步。