Java併發---併發理論

xiang發表於2020-08-14

一、如何理解執行緒安全

在多執行緒中稍微不注意就會出現執行緒安全問題,那麼什麼是執行緒安全問題?

我的認識是。在多執行緒下程式碼執行的結果和預期的正確的結果不一致,該程式碼就是執行緒不安全的,否則就是執行緒安全的

在深入理解Java虛擬機器一書中看到的定義時

當多個執行緒訪問同一個物件時,如果不用考慮這些執行緒在執行環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲取正確的結果,那麼這個物件時執行緒的

在多執行緒條件下,多個執行緒肯定會相互協作完成一件事,一般來說就會涉及到多個執行緒將相互通訊告知彼此狀態以及當前執行結果等,另外為了效能優化,還會涉及到編輯器指令重排序和處理器指令重排序

二、併發程式設計中的主要解決哪兩個問題

1、執行緒之間如何通訊

通訊是指執行緒之間以何種機制來交換資訊,主要有兩種:共享記憶體和訊息傳遞

2、執行緒之間如何完成同步(這裡的執行緒指的是併發執行的活動實體)

Java記憶體模型是共享記憶體的併發模型,執行緒之間主要通過讀-寫共享變數來完成隱式通訊。

三、JVM中的共享變數和執行緒獨佔變數

共享變數

在Java中所有例項域,靜態域和陣列元素都是放在堆記憶體中(所有執行緒均可以訪問到,是可以共享的)。

執行緒獨佔

區域性變數,方法定義引數和異常處理器引數不會線上程間共享

共享資料會出現執行緒安全的問題,而非共享資料不會出現執行緒安全的問題

四、描述一下JMM的抽象結構模型

我們知道CPU的處理速度和主存的讀寫速度不是一個量級的,為了平衡這種巨大的差距,每個CPU都會有快取。因此,共享變數會先放在主存中,每個執行緒都有屬於自己的工作記憶體,並且會把位於主存中的共享變數拷貝到自己的工作記憶體中,之後讀寫操作均使用位於工作記憶體的變數副本,並且在某個時刻將工作記憶體的變數副本寫到主存中去。JMM就從抽象層定義了這種方式,並且JMM決定了一個執行緒對共享變數的寫入何時對其他執行緒是可見的。

 如圖為JMM抽象示意圖,執行緒A和執行緒B 之間要完成通訊的話,要經理如下兩步:執行緒A從主記憶體中將共享變數讀入執行緒A 的工作記憶體後並進行操作,之後將資料重新寫會主記憶體中;執行緒B從主記憶體中讀取最新的共享變數。

從橫向去看,執行緒A和執行緒B好像通過共享變數在進行隱式通訊。這其中就有個問題,如果執行緒A更新後資料並沒有及時的寫回,而此時執行緒B讀到了過期的資料,這就出現了髒讀現象,可以通過同步機制(控制不同執行緒操作發生的相對順序)來解決或者通過valatitl關鍵字是的每次volatitl變數都能夠強制重新整理到主存,從而對每個執行緒都是可見的

五、由於JMM,多執行緒情況下可能出現哪些問題

從上面記憶體抽象結構來說,可能出現資料髒讀的現象,這就是資料可見性的問題。另外,重排序在多執行緒中不注意的話也容易存在一些問題,比如一個經典的問題DCL(雙重檢驗鎖),這就是需要禁止重排序。另外,在多執行緒下原子操作例如:i++不加註意也容易出現執行緒安全的問題。

單總的來說,在多執行緒開發時需要從原子性、有序性、可見性三個方面進行考慮。

J.U.C包下的併發工具類和併發容器也是需要花時間去掌握的。

相關文章