多線程的知識點是一個龐大的體現,對此也是隻知其一;不知其二。一直想系統的深刻的學習多線程的知識,奈何一直沒有找到機會,好吧,其實就是懶。最近在項目中接觸到一個多併發的項目,在項目中踩了無數的坑。在此下定決心作一個併發的學習筆記。html
當兩個線程同時對一個共享可變變量進行操做時,例如:緩存
兩個線程對變量i=1同時執行i++操做。執行完畢後i可能並不等於3而是等於2。由於i++不是原子性的操做,i++其實是有三個步驟安全
第一步:讀取,從主內存中將i=1讀取到本地內存中。多線程
第二步:修改,i自增。併發
第三部:寫入,將i=2寫會到緩存中。性能
因此當兩個線程同時將i讀取到工做內存中,並分別將變量i賦值爲2。學習
原子性是指一個操做是不可中斷的,要麼所有執行成功要麼所有執行失敗,有着「同生共死」的感受。及時在多個線程一塊兒執行的時候,一個操做一旦開始,就不會被其餘線程所幹擾。測試
可見性是指當一個線程修改了共享變量後,其餘線程可以當即得知這個修改。爲何要這樣說?難道一個線程修改了共享變量其餘線程不必定會當即得知這個變量的修改?沒錯事實確實如此。 簡單的舉一個例子。 spa
數據 i 是存儲在主內存中的,當一個線程執行 i++ 操做的時候首先將 i 從主內存讀取到本身線程的工做內存中(也就是緩衝行),而後將工做內存的 i 執行+1操做。若是是單線程程序,在沒有其餘寫入操做的狀況下讀取這個值,首先會讀取緩衝行,緩存命中。那麼總能獲得 +1 操做以後的值。操作系統
可是多線程環境結果則會違背咱們的直覺。
因爲操做系統的執行,咱們並不知道工做內存中的值什麼時候才能被寫入到主內存中(理由很簡單,咱們不可能每次修改了緩存,操做系統就會將值瞬間刷入到主內存吧?這樣效率會多低呀)。因此若是這以前另外一個線程從主內存讀取 i 的值到本地工做內存中。那麼他可能並不會感知到另外一個線程其實已經修改了 i 的值。
爲何synchronized和volatile能夠實現可見性咱們在後續會繼續介紹。
在執行程序時,爲了提升性能,編譯器和處理器經常會對指令作重排序。
爲何要進行重排序?
好比三個操做之間是沒有邏輯關係的,那麼是一個cpu串行執行三個操做快仍是將三個操做分別給三個cpu同時執行快呢?答案顯而易見。
可是帶來的一個弊端就是,可能代碼的執行順序與咱們的意願相違背。
如何讓程序具有有序性,咱們在後續會繼續介紹。
1.不在線程之間共享該狀態變量。
2.將狀態變量修改成不可變的變量。
3.在訪問狀態變量時使用同步。
在介紹原理以前咱們須要瞭解什麼是CAS自旋轉,CAS自旋也就是咱們常說的樂觀鎖,他不會發生線程阻塞,當咱們將修改後的共享變量寫回內存的時候,會檢查在此期間這個共享變量是否被別的線程操做,若是被別的線程操做了,那麼就回寫內存失敗,從新執行代碼。(這樣的好處在於對於同步塊執行時間較短,上下文切換的代價是很是大的)
鎖一共有4個狀態,級別從低到高依次是:無所狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,這幾個狀態會隨着競爭逐漸升級,可是不能降級。而且這4個狀態是存儲在對象頭中的。對象頭中的Mark Word信息以下圖所示(每一行表明一個狀態)
在博客中發現一個大佬畫的圖仍是蠻詳細的,你們能夠參考參考
原文出處:https://www.cnblogs.com/zhxiansheng/p/10611994.html