java并发之初识
一:并發編程的難點
1:原子性問題
- 操作系統做任務切換,可以發生在任何一條CPU指令執行完成后;
- CPU能保證的原子操作是指令級別的,而不是高級語言的操作符;
n++不是原子操作的,而是3條指令
2:可見性問題
- 可見性是指一個線程對一個變量進行修改,另外一個線程可以看的到
- 可見性問題是由CPU的緩存導致的,多核CPU均有各自的緩存,這些緩存要與內存進行同步。(其實就是多線程環境下,一個線程對一個變量的改變了,而另一個線程沒看到,那么的話還是按照原來的變量的值進行計算的話,那么就會出錯)。
3:有序性問題
- 在執行程序時,為了提高性能,編譯器和處理器常常會對指令做重排序;
- 重排序不會影響單線程的執行結果,但是在并發情況下,可能會出現詭異的BUG。
二:并發編程
1:并發編程的目標
解決多核多線程下,造成的 緩存不一致問題,指令重排問題,處理器優化問題
- 在cpu和主存之間添加緩存,在多線程下會存在緩存一致性問題(可見性問題)
- 處理器內部為了使運算單元盡可能的被充分利用,處理器可能會對輸入的代碼進行亂序處理。這就是處理器優化。(原子性問題)
- 很多編程語言的編譯器也會有類似的優化,比如Java虛擬機的即時編譯器(JIT)也會做指令重排。(指令重排問題)
2:并發編程的內存模型
- 為了保證共享內存的正確性(可見性、有序性、原子性),內存模型定義了共享內存系統中多線程程序讀寫操作行為的規范。
- 通過這些規則來規范對內存的讀寫操作,從而保證指令執行的正確性。它與處理器有關、與緩存有關、與并發有關、與編譯器也有關。他解決了CPU多級緩存、處理器優化、指令重排等導致的內存訪問問題,保證了并發場景下的一致性、原子性和有序性。
3:這個內存模型是什么
- JMM是Java Memory Model的縮寫,Java線程之間的通信由JMM控制,即JMM決定一個線程對共享變量的寫入何時對另一個線程可見。
- JMM定義了線程和主內存之間的抽象關系,通過控制主內存與每個本地內存(抽象概念)之間的交互,JMM為Java程序員提供了內存可見性的保證。
- JMM是一種規范,目的是解決由于多線程通過共享內存進行通信時,存在的本地內存數據不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。
4:源代碼和指令間的重排序
為了提高性能,編譯器和處理器常常會對指令做重排序。重排序有3種類型,其中后2種都是處理器重排序。這些重排序可能會導致多線程程序出現內存可見性問題。
- 1.編譯器優化重排序:編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序。
- 2.指令級并行重排序:現代處理器采用了指令級并行技術來將多條指令重疊執行,如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序。
- 3.內存系統的重排序:由于處理器使用緩存和讀/寫緩沖區,這使得加載和存儲操作看上去可能是在亂序執行。
5:解決CPU帶來的重排序
- CPU內存屏障:
- 1.LoadLoad:禁止讀和讀的重排序;
- 2.StoreStore:禁止寫和寫的重排序,
- 3.LoadStore:禁止讀和寫的重排序,
- 4.StoreLoad:禁止寫和讀的重排序。
- Java內存屏障:
6:解決編譯器帶來的重排序
(1):如何解決
JMM使用nappens-before規則來闡述操作之間的內存可見性,以及什么時候不能重排序。在JMM中如果一個操作執行的結果需要對另一個操作可見,那么這兩個操作之間必須要存在nappens-.before:關系。換個角度來說,如果A happens-before B,則意味著A的執行結果必須對B可見,也就是保證跨線程的內存可見性。其中,前4條規則與程序員密切相關。
- 1.程序順序規則:一個線程中的每個操作,happens-before于(對…可見)該線程中的任意后續操作,
- 2.volatile?變量規則:對一個volatile域的寫,happens-before于任意后續對這個volatile域的讀,
- 3.synchronized規則:對一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖,
- 4.傳遞性:若A happens-.before B,且B happens-before C,則A happens-before C,
- 5.start()規則:若線程A執行Thread.start(0,則線程A的start()操作nappens-before于線程B中的任意操作,
- 6.join規則:若線程A執行ThreadB.join0并成功返回,那么線程B中的任意操作happens-.before于線程A從ThreadB.join0的成功返回。
(2):關鍵字vlatile
- 4.1 volatile的基本特性
- 可見性:對一個volatile變量的讀,總是能看到對這個volatile變量最后的寫入:
- 原子性:對任意單個volatile變量的讀/寫具有原子性,但類似volatile++這種復合操作不具有原子性。
- 4.2 volatilet的內存語義
- 寫內存語義:當寫一個volatile變量時,JMM會把該線程本地內存中的共享變量的值刷新到主內存
- 讀內存語義:當讀一個volatile變量時,JMM會把該線程本地內存置為無效,使其從主內存中讀取共享變量。
總結
- 上一篇: 利用结构体数组实现重排序(详解)
- 下一篇: java并发之synchronized实