Java多線程學習(一)---並發與多線程


Java並發與多線程

摘要:

1. 並發與並行的區別,何為並發編程,並發編程的優勢在哪

2. 多線程、多任務、多進程機制概述

3. 多線程、多任務、多進程機制與編程思想的關系

一、並發

1.1 並發與並行

首先介紹一下並發與並行,兩者雖然只有一字之差,但實際上卻有着本質的區別,其概念如下:

並行性(parallel):指在同一時刻,有多條指令在多個處理器上同時執行;

並發性(concurrency):指在同一時刻只能有一條指令執行,但多個進程指令被快速輪換執行,使得在宏觀上具有多個進程同時執行的效果。

1.2 順序編程與並發編程

我們在解決編程問題時,通常使用順序編程來解決,即程序中的所有事物在任意時刻都只能執行一個步驟。然而對於某些問題,我們希望能夠並行地執行程序中的多個部分,來達到我們想要的效果。在單處理器機器中,我們可以將程序划分為多個部分,然后每個部分由該處理器並發執行。在多處理器機器中,我們可以將程序划分多個部分,然后每個部分分別在多個處理器上並行執行。當然為了更加充分利用CPU資源,我們也可以在多個處理器上並發執行,那么在這我們就涉及到了另一種編程模式了並發編程。並發編程又叫多線程編程。並發編程使我們可以將程序划分為多個分離的、獨立運行的任務。通過使用多線程機制,每個獨立任務都將由線程來驅動。一個線程就是在進程中的一個單一的順序控制流,單個進程可以擁有多個"並發執行"的任務。這樣使程序的每個任務,都好像擁有一個自己的CPU一樣。但其底層機制還是是切分CPU時間,CPU都有個時鍾頻率,表示每秒中能執行CPU指令的次數。在每個時鍾周期內,CPU實際上只能去執行一條也有可能多條指令。操作系統將進程進行管理,輪流分配每個進程很短的一段是時間但不一定是均分,然后在每個進程內部,程序代碼自己處理該進程內部線程的時間分配,多個線程之間相互的切換去執行,這個切換時間也是非常短的所以通常我們不需要考慮它。

並發是指"發",不是處理,最常見的情況就是許多人在一小段時間內都點擊了你的網站,發出了處理請求。並發編程是對並發狀況的應對,在單處理器和多處理器機器上都可對其進行應對,可這個處理方案和架構以及算法有關。CPU一般是分時的,會在極短的時間內不停地切換給不同的線程使用,無論多少並發都會處理下去,只是時間問題,如何提高處理效率就看采用的技術了。

1.3 並發編程的優勢

並發編程可以使我們的程序執行速度得到提高,例如,如果你有一台多處理器的機器,那么就可以在這些處理器之間分布多個任務,從而可以極大地提高吞吐量。這是Web服務器的常見情況,一般Web服務器是一個多處理器機器,將為每個請求分配到一個線程中,那么就可以將大量的用戶請求分布到多個CPU上進行並發處理。

但是,並發通常是提高運行在單處理器上的程序的性能。雖然,在單處理器上運行的並發程序開銷確實應該比該程序的所有部分都順序執行開銷,因為其中增加了所謂上"下文切換"的代價,即從一個任務切換到另一個任務。表面上看,將程序的所有部分當作單個的任務運行好像是開銷更小一點,並且可以節省上下文切換的代價。但是我們的程序並不會按我們設想的那樣一直正常運行,它會發生阻塞。如果程序中的某個任務因為某些原因發生了阻塞,那么該任務將不能繼續執行。如果沒有並發,則整個程序都將停止下來,直至外部條件發生變化。但是,如果使用並發來編寫程序,那么當一個任務阻塞時,程序中的其他任務還可以繼續執行,因此這個程序可以保持繼續向前執行,這樣就提高程序的執行效率和運行性能。

並發需要付出代價,包含復雜性代價,但是這些代價與在程序設計、資源負載均衡以及用戶方便使用方面的改進相比,就顯得微不足道了。通常,線程使你能夠創建更加松散耦合的設計則,你的代碼中各個部分都必須顯式地關注那些通常可以由線程來處理的任務。

二、多任務、多進程、多線程

幾乎所有的操作系統都支持同時運行多個任務,一個任務通常就是一個程序,每個運行中的程序就是一個進程。當一個程序運行時,內部可能包含了多個順序執行流,每個順序執行流就是一個線程

2.1 多進程

實現並發最直接的方式是在操作系統級別使用進程,進程是運行在它自己的地址空間內的自包容的程序。多任務操作系統可以通過周期性地將CPU從一個進程切換到另一個進程,來實現同時運行多個進程。 盡管對於一個CPU而言,它在某個時間點只能運行一個進程,但CPU可以在多個進程之間進行輪換執行,並且CPU的切換速度極高,使我們無法感知其切換的過程,就好像有多個進程在同時執行。

幾乎所有的操作系統都支持進程的概念,所有運行中的任務通常對應一個進程(Process)。當一個程序進入內存運行時,即變成一個進程。進程是處於運行過程中的程序,並且具有一定的獨立功能,進程是系統進行資源分配調度的一個獨立單位。一般而言,進程包含如下3個特征

獨立性:進程是系統中獨立存在的實體,它可以擁有自己獨立的資源,每一個進程都擁有自己私有的地址空間。在沒有經過進程本身允許的情況下,一個用戶進程不可以直接訪問其他進程的地址空間。

動態性:進程與程序的區別在於,程序只是一個靜態的指令集合,而進程是一個正在系統中活動的指令集合。在進程中加入了時間的概念,進程具有自己的生命周期和各種不同的狀態,這些概念在程序中部是不具備的。

並發性:多個進程可以在單個處理器上並發執行,多個進程之間不會互相影響。

現代的操作系統都支持多進程的並發,但在具體的實現細節上可能因為硬件和操作系統的不同而采用不同的策略。比較常用的方式有:共用式的多任務操作策略,例如Windows 3.1和Mac OS 9。目前操作系統大多采用效率更高的搶占式多任務操作策略,例如 VVindows NT、Windows 2000以及UNIX/Linux等操作系統。但對進程的並發通常會有數量和開銷的限制,以避免它們在不同的並發系統之間的可應用性。為了應對該問題,所以在多進程的基礎上提出了多線程的概念,下面將詳細介紹。

2.2 多線程

2.2.1 多線程概述

多線程則擴展了多進程的概念。使得同一個進程中也可以同時並發處理多個任務。線程(Thread)也被稱作輕量級進程(Lightweight Process)。線程是進程的執行單元,就像進程在操作系統中的地位一樣,線程在程序中是獨立的、並發的執行流。當進程被初始化后,主線程就被創建了。對於絕大多數的應用程序來說,通常僅要求有一個主線程,但也可以在該進程內創建多條順序執行流,這些順序執行流就是線程,每個線程也是互相獨立的。

線程是進程的組成部分,一個進程可以擁有多個線程,一個線程必須有一個父進程。線程可以擁有自己的堆棧、自己的程序計數器和自己的局部變量,但不擁有系統資源,它與父進程的其他線程共享該進程所擁有的全部資源。因為多個線程共享父進程里的全部資源,因此編程更加方便;但必須更加小心,我們必須確保線程不會妨礙同一進程里的其他線程。

2.2.2 多線程機制

線程模型為編程帶來了便利,它簡化了在單一程序中同時交織在一起的多個操作的處理。在使用線程時,CPU將輪流給每個任務分配其占用時間。每個任務都覺得自己在一直占用CPU,但事實上CPU時間是划分成片段分配給了所有的任務。線程的一大好處是可以使你從這個層次抽身出來,即代碼不必知道它是運行在具有一個還是多個CPU的機器上。所以,使用線程機制是一種建立透明的、可擴展的程序的方法,如果程序行得太慢,為機器增添一個CPU就能很容易地加快程序的運行速度。多任務和多線程往往是使用多處理器系統的最合理方式。

2.2.3 多線程調度

線程可以完成一定的任務,可以與其他線程共享父進程中的共享變量部分環境,相互之間協同來完成進程所要完成的任務。線程是獨立運行的,它並不知道進程中是否還有其他線程存在,線程的執行搶占式的,也就是說,當前運行的線程在任何時候都可能被掛起,以便另外一個線程可以運行。

一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以並發執行。從邏輯角度來看,多線程存在於一個應用程序中,讓一個應用程序中可以有多個執行部分同時執行,但操作系統無須將多個線程看作多個獨立的應用,對多線程實現調度和管理以及資源分配。線程的調度管理進程本身負責完成。

歸納起采可以這樣說:操作系統可以同時執行多個任務,每個任務就是進程;進程可以同時執行多個任務,每個任務就是線程。簡而言之,一個程序運行后至少有一個進程,一個進程里可以包含多個線程,但至少要包含一個線程。

2.2.4 多線程的優勢

線程在程序中是獨立的、並發的執行流,與分隔的進程相比,進程中線程之間的隔離程度要小

01. 它們共享內存文件句柄和其他每個進程應有的狀態。因為線程的划分尺度小於進程,使得多線程程序的並發性高。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率

02. 線程比進程具有更高的性能,這是由於同一個進程中的線程都有共性----多個線程共享同一個進程虛擬空間。線程共享的環境包括進程代碼段進程的公有數據等。利用這些共享的數據,線程很容易實現相互之間的通信

03. 當操作系統創建一個進程時,必須為該進程分配獨立的內存空間,並分配大量的相關資源;但創建一個線程則簡單得多,因此使用多線程來實現並發比使用多進程實現並發的性能要高得多

總結起來,使用多線程編程具有如下幾個優點:

01. 進程之間不能共享內存,但線程之間共享內存非常容易

02. 系統創建進程時需要為該進程重新分配系統資源,但創建線程則代價小得多,因此使用多線程來實現多任務並發比多進程的效率高

03. Java語言內置了多線程功能支持,而不是單純地作為底層操作系統的調度方式,從而簡化了Java的多線程編程

在實際應用中,多線程是非常有用的,一個瀏覽器必須能同時下載多個圖片;一個Web服務器必須能同時響應多個用戶請求;Java虛擬機本身就在后台提供了一個超級線程來進行垃圾回收;圖形用戶界面(GUI)應用也需要啟動單獨的線程從主機環境收集用戶界面事件……總之,多線程在實際編程中的應用是非常廣泛的。

如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】。
如果,您希望更容易地發現我的新博客,不妨點擊一下左下角的【關注我】。
如果,您對我的博客所講述的內容有興趣,請繼續關注我的后續博客,我是【Sunddenly】。

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM