Java並發—並發理論
一、如何理解線程安全
在多線程中稍微不注意就會出現線程安全問題,那麼什麼是線程安全問題?
我的認識是。在多線程下代碼執行的結果和預期的正確的結果不一致,該代碼就是線程不安全的,否則就是線程安全的
在深入理解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包下的並發工具類和並發容器也是需要花時間去掌握的。