天天看點

【3】Java并發程式設計:多線程基本概念

Java并發程式設計一直是開發學習中的重點和難點,難在于許多的基本概念不是短時間内就容易了解。下面就Java并發程式設計中的涉及多線程的基本概念做一個總結:

1、TPS(Transactions Per Second,每秒事務處理數)

: 一秒内服務端平均能響應的請求總數
           

2、高速緩存Cache

3、Java記憶體模型(Java Memory Model):變量、工作記憶體、主記憶體

4、Java記憶體模型:記憶體間互動操作

lock(鎖定):作用于主記憶體的變量,它把一個變量辨別為一條線程獨占的狀态。

unlock(解鎖):作用于主記憶體變量,它把一個處于鎖定狀态的變量釋放出來,釋放後的變量才能被其他線程鎖定。

read(讀取):作用于主記憶體變量,它把一個變量的值從主記憶體傳輸到線程的工作記憶體中,以便随後的load動作使用。

load(載入):作用于工作記憶體變量,它把read操作從主記憶體中得到的變量值放入工作記憶體的變量副本中。

use(使用):作用于工作記憶體變量,它把工作記憶體中一個變量的值傳遞給執行引擎,每當虛拟機遇到一個需要使用到變量的值的位元組碼指令時将會執行這個操作。

assign(指派):作用于工作記憶體變量,它把一個從執行引擎接收到的值賦給工作記憶體的變量,每當虛拟機遇到一個給變量指派的位元組碼指令時執行這個操作。

store(存儲):作用于工作記憶體變量,它把工作記憶體中的一個變量的值傳送到主記憶體中,以便随後的write操作使用。

write(寫入):作用于主記憶體變量,它把store操作從工作記憶體中得到的變量中的值放入主記憶體的變量中。
           

執行時滿足以下原則:

不允許read和load、store和write操作之一單獨出現,即不允許一個變量從主記憶體讀取了單工作記憶體不接受,或者從工作記憶體發起回寫了但主記憶體不接受的情況出現,

不允許一個線程丢棄它的最新的assign操作,即變量在工作記憶體中改變了之後必須該變化同步回主記憶體。

不允許一個線程無原因的(沒有發生過任何assign操作)把資料從線程的工作記憶體同步回主記憶體。

一個新的變量隻能在主記憶體中“誕生”,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變量;換句話說,就是對一個變量實施use、store操作之前,必須先執行過了assign和load操作。

一個變量在同一個時刻隻允許一條線程對其進行lock操作,但lock操作可以被同一條線程重複執行多次,多次執行lock操作後,隻有執行相同次數的unlock操作,變量才會被解鎖。

如果對一個變量執行lock操作,那将會情況工作記憶體中此變量的值,在執行引擎使用這個變量前,需要重新執行load或assign操作初始化變量的值。

如果一個變量事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個被其它線程鎖定住的變量。

對一個變量執行unlock之前,必須先把次變量同步回主記憶體中。
           

5、long與double的非原子性協定: 允許虛拟機對沒有被volatile修飾的64位資料的讀寫操作劃分為2此32位的操作來進行,即允許虛拟機實作選擇可以不保證64位資料類型的load、store、read和write這4個操作的原子性。

6、線程的原子性、可見性、有序性

原子性:由JAVA的記憶體模型來直接保證的原子性變量操作包括read、load、assign、use、store、write。基本資料類型的通路讀寫是都具有原子性的。如果需要一個更大範圍的原子性保證,則可以用同步代碼塊synchronized關鍵字。

可見性:指當一個線程修改了共享變量的值,其他線程能夠立即得知這個修改。volatile、final、synchronized等三個關鍵字能實作可行性。

有序性(須了解指令重排序):java的天然有序性是如果再本線程内觀察,所有的操作都是有有序的;如果再一個線程中觀察另一個線程,所有的操作都是無序的。volatile、synchronized兩個關鍵字可保證線程之間操作的有序性。

7、先行發生原則:Java記憶體模型中定義的兩項操作之間的偏序關系。java記憶體模型定義了一些先行規則,如兩個操作之間的關系不在此列,則它們之間就沒有順序性的保障,虛拟機可以随意對它們進行重排序。

程式次序規則:在一個線程内,按照程式代碼順序,書寫在前面的操作先行發生于書寫在後面的操作。準确的說,應該是控制流順序而不是程式代碼的順序,因為要考慮循環分之的結構。

管程鎖定規則:一個unlock操作線程發生于後面對同一個鎖的lock操作。這裡必須強調的是同一個鎖,而“後面”是指時間上的先後順序。

volatile變量規則:對一個volatile變量的寫操作先行發生于後面對這個變量的讀操作。

線程啟動規則:thread對象的start()方法先行發生于此線程的每一個動作。

線程終止規則:線程中的所有操作都先行發生于對此線程的終止監測我們可以通過thread.join()方法結束、Thread.isActive()的傳回值等手段監測到線程已終止執行。

線程中斷規則:對線程interrupt()方法的調用先行發生于被中斷線程的代碼監測到中斷事件的發生,可以通過Thread.interrupted()方法監測到是否有中斷發生。

對象終結規則:一個對象的初始化完成(構造函數執行結束)先行發生于它的finalize()方法的開始。

傳遞性:如果操作A線程發生于操作B,操作B線程發生操作C,那就可以得出操作A線程發生于操作C的結論。

8、java線程:是基于作業系統原生線程模型來實作的。線程模型有:使用核心線程實作、使用使用者線程實作、使用使用者線程加輕量級程序混合實作。

9、java線程排程:使用的是搶占式線程排程,與搶占式任務排程對應的是協同式任務排程(如一個線程出錯,則可能對整個系統崩潰)。

協同式任務排程:線程的執行時間由線程本身控制,線程把自己的工作執行完成之後,要主動通知系統切換到另外一個線程上。

搶占式任務排程:每個線程将由系統來配置設定執行時間,線程的切換不由線程本身決定。在java中可以設定線程的優先級,優先級越高的則容易被系統選擇執行。

10、線程狀态

【3】Java并發程式設計:多線程基本概念

11、JAVA線程安全可分為:不可變(final修飾的基本類型,對象則描述為任何行為對不會對其狀态産生影響,如String)、絕對線程安全、相對線程安全、線程相容、線程對立。

12、線程安全方法

互斥同步:又稱悲觀鎖。同步是指在多個線程并發通路共享資料時,保證共享資料在同一個時刻隻被一個線程使用。而互斥是實作同步的一種手段,臨界區、互斥量、信号量都是主要的互斥實作方式。

synchronized關鍵字

java.until.concurrent下的ReentrantLock來實作同步,其實作了一些進階功能:

等待可中斷:當持有鎖的線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待,改為處理其他事情,可中斷特性對于處理執行時間非常長的同步快很有幫助。

可實作公平鎖:多個線程在等待同一個鎖時,必須按照申請鎖的時間順序依次獲得鎖。而不公平鎖則保證這一點,synchronized中的鎖時非公平的,ReentrantLock預設也是非公平的,但可以通過構造函數的布爾值來要求使用公平鎖。

鎖可以綁定多個條件:一個ReentrantLock對象可以同時綁定多個Condition對象,而synchronized中,鎖對象的wait()、notify()、notifyAll()方法可以實作一個隐含的條件,如有多個條件則需要額外的田間synchronized。

在jdk6以前的版本中,可用ReentrantLock對象,其性能較好。但在jdk6及以後的版本中對synchronized做了相關優化,性能相差不多,建議在高版本中使用synchronized對象。

非阻塞同步:基于沖突監測的樂觀并發政策,就是先進行操作,如果沒有其他線程争用共享資料,那操作就成功了;如果共享資料有争用,産生了沖突,那就采取其他補償措施。可以參考sun.misc.Unsafe;在java.until.concurrent下有一些類實作了此Unsafe機制。如AtomicInteger。

無同步方案:如果不涉及到資料共享,則無須任何同步措施保證正确性。

可重入代碼:可以在代碼執行的任意時刻中斷它,轉而去執行另外一段代碼,而在控制權傳回後,原來的程式不會出現任何的錯誤。

線程本地存儲:如果一段代碼中所需要的資料必須與其它代碼共享,那就看看這些共享資料的代碼是否能保證在同一線程中執行?如果能保證,我們就可以把共享資料的可見範圍限制在同一個線程之類,這樣無須同步也能保證線程之間不出現資料争用的問題。需要對ThreadLocal對象進行了解。

參考緻謝:

1 http://my.oschina.net/aiyungui/blog/488651