-
1 # 沉迷翻新
-
2 # 篠羽
多執行緒:在瞭解執行緒之前,要先知道程序這個概念。程序是一個具有獨立功能的程式關於某個資料集合的一次執行活動。簡單點說,程序就是執行中的程式活動,是一個活動的實體。多程序,就好比同時打開了Word,Excel和Visio,他們都是不同的程式執行活動,即多個程序同時啟動而已,這個概念比較好理解。執行緒,是一個執行中的程式活動(即程序)的多個執行路徑,執行排程的單位。執行緒依託於程序存在,在程序之下,可以共享程序的記憶體,而且還擁有一個屬於自己的記憶體空間,這段記憶體空間也叫做執行緒棧,是在建立執行緒時由系統分配的,主要用來儲存執行緒內部所使用的資料。多執行緒,指在一個程序下有多個執行緒。各個執行緒執行自己的任務,這些執行緒可以”同時進行“(這裡加了雙引號,下面會講述到加雙引號的原因)。那多執行緒有什麼好處?多執行緒應用在生活中隨處可見,Word文件就是一個很好的例子。Word有“後臺列印”的功能,使用者點選列印按鈕後,如果發現可以對當前文字進行修改,可以在列印過程中回到主介面進行修改、儲存等操作。 如果沒有應用多執行緒,不妨假設使用者要列印的文字很長很長,那麼使用者要等列印操作執行完後,才可以對文字進行修改編輯儲存等,這樣使用者體驗就不如多執行緒的好。還有迅雷,有沒有發現迅雷是可以同時下載東西的?例如同時下載A,B,A下載進度到53.4%,B下載進度到47.1%,有時A速度快些,有時B速度快些,反正能確定的是A,B都在下載內容,而不是一定要等A下載完後,B才可以開始下載,這也是多執行緒的作用。因此,多執行緒強調”同時,一起進行“,而不是單一的順下操作。
併發:Concurrency,是併發的意思。併發的實質是一個物理CPU(也可以多個物理CPU) 在若干道程式(或執行緒)之間多路複用,併發性是對有限物理資源強制行使多使用者共享以提高效率。微觀角度:所有的併發處理都有排隊等候,喚醒,執行等這樣的步驟,在微觀上他們都是序列被處理的,如果是同一時刻到達的請求(或執行緒)也會根據優先順序的不同,而先後進入佇列排隊等候執行。宏觀角度:多個幾乎同時到達的請求(或執行緒)在宏觀上看就像是同時在被處理。通俗點講,併發就是隻有一個CPU資源,程式(或執行緒)之間要競爭得到執行機會。圖中的第一個階段,在A執行的過程中B,C不會執行,因為這段時間內這個CPU資源被A競爭到了,同理,第二個階段只有B在執行,第三個階段只有C在執行。其實,併發過程中,A,B,C並不是同時在進行的(微觀角度)。但又是同時進行的(宏觀角度)。
並行:Parallelism,即並行,指兩個或兩個以上事件(或執行緒)在同一時刻發生,是真正意義上的不同事件或執行緒在同一時刻,在不同CPU資源呢上(多核),同時執行。並行,不存在像併發那樣競爭,等待的概念。圖中,A,B,C都在同時執行(微觀,宏觀)。
透過多執行緒實現併發,並行:java中的Thread類定義了多執行緒,透過多執行緒可以實現併發或並行。在CPU比較繁忙,資源不足的時候(開啟了很多程序),作業系統只為一個含有多執行緒的程序分配僅有的CPU資源,這些執行緒就會為自己儘量多搶時間片,這就是透過多執行緒實現併發,執行緒之間會競爭CPU資源爭取執行機會。在CPU資源比較充足的時候,一個程序內的多執行緒,可以被分配到不同的CPU資源,這就是透過多執行緒實現並行。至於多執行緒實現的是併發還是並行?上面所說,所寫多執行緒可能被分配到一個CPU核心中執行,也可能被分配到不同CPU執行,分配過程是作業系統所為,不可人為控制。所有,如果有人問我我所寫的多執行緒是併發還是並行的?我會說,都有可能。不管併發還是並行,都提高了程式對CPU資源的利用率,最大限度地利用CPU資源。
-
3 # 梅涼欣
瞭解幾個點就夠了
1、併發與並行的區別
2、為什麼我們需要併發
3、java是如何實現併發的
-
4 # 程式設計要實踐
要理解併發的含義,首先要知道併發產生的原因。當多個CPU處理器同時執行一條寫操作指令時,就會因為CPU在執行過程中會存在穿插執行的可能,從而造成資料紊亂(如果只有讀操作,是沒有併發問題的)。為了解決穿插執行的問題,CPU就提供了機制來解決併發問題。
只有CPU提供了原子性指令,上層應用才能夠根據這些指令來設計出指令段與指令段之間的原子性操作。這是一種自底向上的設計,沒有CPU最底層的支援,上層應用根本就無法解決併發問題。應用程式使用自身語言提供的併發操作函式庫,比如java的juc包,而這些函式庫又會封裝OS的系統呼叫或者使用glibc庫,OS的系統呼叫最終會使用CPU提供的原子性指令。
可以看看下面這兩篇文章,講解了CPU是如何支援併發的,上層語言的併發函式庫是對底層的封裝:
併發原理—CPU原子性指令(一)
併發原理—如何保證多條指令的原子性(二)
回覆列表
一、多執行緒三大特性
多執行緒有三大特性:原子性、可見性、有序性。
原子性(跟資料庫的事務特性中的原子性類似,資料庫的原子性體現是dml語句執行後需要進行提交):理解:即一個操作或多個操作,要麼全部執行並且執行的過程中不會被任何因素打斷,要麼都不執行。一個很經典的例子就是銀行賬戶轉賬問題:比如從賬戶A向賬戶B轉5000元,那麼必然包括2個操作:從賬戶A減去5000元,往賬戶B加上5000元。這2個操作必須要具備原子性才能保證不出現一些意外的問題。我們操作資料也是如此,比如i = i+1;其中就包括,讀取i的值,計算i,寫入i。這行程式碼在Java中是不具備原子性的,則多執行緒執行肯定會出問題,所以也需要我們使用同步synchronized和lock鎖這些東西來確保這個特性了。原子性其實就是保證資料一致、執行緒安全一部分,
可見性:可見性是與java記憶體模型息息相關的。當多個執行緒訪問同一個變數時,一個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。若兩個執行緒在不同的cpu,那麼執行緒1改變了i的值還沒重新整理到主存,執行緒2又使用了i,那麼這個i值肯定還是之前的,執行緒1對變數的修改執行緒2沒有看到,這就是可見性問題。
有序性:理解:程式執行的順序按照程式碼的先後順序執行。一般來說,處理器為了提高程式執行效率,可能會對輸入程式碼進行最佳化,它不保證程式中各個語句的執行先後順序同程式碼中的順序一致,但是它會保證程式最終執行結果和程式碼順序執行的結果是一致的。
二、Java記憶體模型jvm的記憶體結構為:堆、棧、方法區,不同於java的記憶體模型,Java的記憶體模型是關於多執行緒相關的。
理解:共享記憶體模型指的是Java記憶體模型(簡稱JMM),JMM決定一個執行緒對共享變數的寫入時,能對另一個執行緒可見。從抽象的角度來看,JMM定義了執行緒和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(main memory)中(區域性變數不會儲存在),每個執行緒都有一個私有的本地記憶體(local memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。它涵蓋了快取、寫緩衝區、暫存器以及其他的硬體和編輯器最佳化。
總結:什麼是Java記憶體模型:java記憶體模型簡稱jmm,定義了一個執行緒對另一個執行緒可見。共享變數存放在主記憶體中,每個執行緒都有自己的本地記憶體,當多個執行緒同時訪問一個數據的時候,可能本地記憶體沒有及時重新整理到主記憶體,所以就會發生執行緒安全問題。
三、Volatile關鍵字Volatile關鍵字的作用:變數在多個執行緒之間可見。
Volatile關鍵字是非原子性的,不能保證資料的原子性,只是能夠把解決立馬重新整理到主記憶體中,不能解決併發問題。
如果想要保證資料的原子性,解決併發問題,需要使用併發包裡的AutomicInteger原子類。
volatile與synchronized區別:僅靠volatile不能保證執行緒的安全性(原子性)。
1.volatile輕量級,只能修飾變數。synchronized重量級,還可修飾方法。2.volatile只能保證資料的可見性,不能用來同步,因為多個執行緒併發訪問volatile修飾的變數不會阻塞。四、TreadLocal1.什麼是ThreadLocal?ThreadLocal提高一個執行緒的區域性變數,訪問某個執行緒擁有自己區域性變數。
當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒對應的副本。
ThreadLocal介面方法有4個:
void set(Object value)設定當前執行緒的執行緒區域性變數的值;public Object get()該方法返回當前執行緒所對應的執行緒區域性變數;public void remove()將當前執行緒區域性變數的值刪除,目的是為了減少記憶體的佔用,該方法是JDK5.0新增的方法。需要指出的是,當執行緒結束後,對應該執行緒的區域性變數將自動被垃圾回收,所以顯式呼叫該方法清除執行緒的區域性變數並不是必須的操作,但它可以加快記憶體的回收速度;protected Object initialValue()返回該執行緒區域性變數的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲呼叫方法,線上程第1次呼叫get()或set(Object)時才執行,並且僅執行1次。ThreadLocal中的預設實現直接返回一個null。2.ThreadLocal底層實現原理:ThreadLocal透過Thread.currentThread();獲取當前執行緒
操作map集合:ThreadLocalMap
void set(Object value)就是Map.put(“當前執行緒”,值);
public Object get()就是獲取ThreadLocalMap然後操作後返回。
五、執行緒池1.為什麼要使用執行緒池?
因為要透過執行緒池來管理執行緒,啟動或者停止一個執行緒非常耗費資源,所以將執行緒交給執行緒池來管理能夠節約記憶體。一般在企業開發當中我們都使用執行緒池,透過spring去整合執行緒池,非同步註解。
2.什麼是執行緒池?
執行緒池是指在初始化一個多執行緒應用程式過程中建立一個執行緒集合,然後在需要執行新的任務時重用這些執行緒而不是新建一個執行緒。執行緒池中執行緒的數量通常完全取決於可用記憶體數量和應用程式的需求。然而,增加可用執行緒數量是可能的。執行緒池中的每個執行緒都有被分配一個任務,一旦任務已經完成了,執行緒回到池子中並等待下一次分配任務。
3.執行緒池作用:
基於以下幾個原因,在多執行緒應用程式中使用執行緒池是必須的:
1.執行緒池改進了一個應用程式的相應時間。由於執行緒池中的執行緒已經準備好且等待被分配任務,應用程式可以直接拿來使用而不用新建一個執行緒。2.執行緒池節省了CLR為每個短生命週期任務建立一個完整的執行緒開銷並可以在任務完成後回收資源。3.執行緒池根據當前在系統中執行的程序來最佳化執行緒時間片。4.執行緒池允許我們開啟多個任務而不用為每個執行緒設定屬性。5.執行緒池允許我們為正在執行任務的程式引數傳遞一個包含狀態資訊的物件引用。6.執行緒池可以用來解決處理一個特定請求最大執行緒數量限制問題。4.執行緒池四種建立方式:
java透過Executors(jdk1.5的併發包)提供四種執行緒池,分別為:
1.newCachedThreadPool 建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。2.newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。3.newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行4.newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO,LIFO,優先順序)執行。總結:newCachedThreadPool 建立的執行緒,執行緒池為無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的執行緒,而不用每次新建執行緒。newFixedThreadPool 每次執行傳入引數大小個執行緒,其他執行緒在等待(企業中用的不多)。newScheduledThreadPool 使用schedule方法建立單位時間的延遲執行緒池。