<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Optimus_7

      博客園 首頁 新隨筆 聯系 訂閱 管理

      1. JMM規定CPU執行的(線程執行的)一些交互操作(應該并不是指令名稱,只是抽象動作概念):

          每條指令都是原子的(指令內部的操作們粘在一起的,不可分開的,要么都執行要么都不執行)

          (JMM規定每條指令都是原子的,但是對double和long的操作除外)

       

          lock:作用于主內存的變量, 將該變量被唯一的線程獨占。對于一個變量而言,會變成單核CPU。

          unlock:作用于主內存的變量,與lock相反。

       

          read:作用于主內存的變量,將變量的值拿在手里。

          load:作用于工作內存的變量,將read操作拿著的變量的值,放入工作內存的變量的拷貝空間中。

       

          store:作用于工作內存的變量,將變量的值拿在手里。

          write:作用于主內存的變量。將store操作操作拿著的變量的值,放入主內存中。

       

          use:作用于工作內存的變量,將變量提供給CPU。

          assign:作用于工作內存的變量,接收CPU的指令,將一個值賦值給變量。

       

          JMM對以上指令的規定:

            1. read-load和store-write,必須成對出現,且每對內部按順序執行。(但是可以不連續執行)

            2. 如果一個線程執行了assign,則后面必須store-write。即 assign+(store-write)

            3. 如果一個線程沒執行assign,則不允許store-write。即 no assign,no(store-write)

            4. 一個線程使用變量(執行use或者store-write)之前,這個變量必須load,(如果load之后變量為null的話還需要assign初始化)

            5. 一個變量只能被同一個線程lock,但是可以lock多次。記得unlock時也要unlock那么多次。

            6. 一個線程執行lock變量,會清空這個變量的在工作內存的值,所以需要lock之后執行load和assign。

            7. 一個線程只能unlock當前線程已經lock了的變量。

            8. 一個線程執行unlock,之前必須store-write同步會主內存。unlock ‘before = (store-write)

       

          JMM允許對double和long的操作可以不需要滿足原子性:

            因為double和long是64位,又因為32位CPU的緩存行是32位,所以有時不可能實現原子性。

            所以JMM允許double和long的操作可以不需要滿足原子性。

            但是現在的商用JVM都已經把double和long的操作實現為原子了。

      2. Volatile

        2.1 Volatile語義1:(可見性語義) 

          Volatile變量改變時會直接寫入主內存。但是只保證可見性,并不保證原子性。

          意思是,只保證數據顯示的值是最新的,但是不保證我用這條數據的時候別人不用,如果大家都用,可能會造成某些人的操作被覆蓋而看不出來。

          例子:以下場景不適合用Volatile:

          新建20個線程,對同一個Volatile變量1萬次++,但是結果小于20萬。

          解決方式:加鎖 或者 CAS的AtomInterger

        2.2 Volatile語義1的推論:

          滿足以下兩個條件時,適合用Volatile。如果不滿足的話還是用Synchronized或者JUC吧:

          1. 運算結果并不依賴變量的當前值,或者能夠確保只有單一的線程修改變量的值。(場景比如:不并發進行++)

          2. 變量不需要與其他的狀態變量共同參與不變約束。(場景比如:用Volatile變量作為開關(且只用這一個Volatile變量),控制某個方法是否執行)

        2.3 Volatile語義2:(有序性語義)

          禁止指令重排序。

          例子:以下場景適合用Volatile:

            Volatile+DCL單例模式。

        2.4 Volatile的實現方式為LOCK指令,相當于內存屏障:

          當變量為Volatile,在改變Volatile變量的值以后,比普通變量時多出一條匯編代碼 ,注意是匯編代碼而不是字節碼。

          lock addl $0x0,(%esp)

          這句話的關鍵是lock,

          加入了lock前綴之后就變成了如下操作:

          1. 對CPU總線加鎖(不過后來的處理器都采用鎖緩存替代鎖總線,因為鎖總線的開銷比較大)

          2. 這時其他CPU的讀寫請求都會被阻塞,直到鎖釋放。

          3. 當前CPU改變當前高速緩存中的Volatile變量的值,強制做了一次store+write操作,寫入主內存。

          4. 鎖釋放,同時清空其他CPU的相應的緩存行(也有人說是設置為無效Invalid)。

          5. 當其他CPU讀取那個Volatile變量時,發現空行或者無效,那怎么辦呢?根據MESI,會從主存獲取。  

          

          使語義1生效的原因:

            即操作12345

          使語義2生效的原因:

            即操作12345,其實整個操作相當于在寫Volatile變量加了全屏障。

       

          ps.

            Volatile只可以修飾成員變量(靜態不靜態都可以)但是不可以修飾局部變量(idea不允許)。因為局部變量只存活于方法棧中(工作內存中)所以不存在主存可見性的問題。

            加了Volatile的成員變量的字節碼的區別是:變量多了一個ACC_VOLATILE的flag而已。

          

          public class test {
              static int i;
              // static Volatile int i;
              void hy () {
                  i = 20;
              }
          }

          

       

      3. 內存屏障

        作用:

          1. 保證數據的可見性

          2. 防止指令之間的重排序

        x86的內存屏障分為三類及其作用:

          內存屏障其實是Intel提供的硬件指令:sfence (Store Barrier)、lfence(Load Barrier)、mfence (Full Barrier)

          Intel還提供了一個lock指令前綴,這個前綴是專門用于加在指令(比如add)之前的,表示當前指令操作的內存只能由當前CPU使用,而且自帶Full Barrior效果;(就是Volatile的實現)

            1. 讀屏障Load Barrier(lfence + 內存地址):先使緩存行失效,然后觸發強制從主存獲取數據的動作。

              保證的是,Load Barrier之前的load和之后的load不會被重排序。

            2. 寫屏障Store Barrier(sfence + 內存地址):觸發強制寫入主存的動作,對其他CPU可見。

              保證的是,Store Barrier之前的store和之后的store不會被重排序。

            3. 全屏障 Full Barrier(mfence + 內存地址):強制從主存讀取以及強制向主存寫。

              保證的是,Full Barrier上面的不能下去,同時,Full Barrier下面的不能上去。

              (全屏障是一個原子效果,并不是等于寫屏障+讀屏障,因為寫屏障+讀屏障這個組合也可能出現重排(因為x86允許Store-Load 重排序))

         X86下僅支持一種指令重排:Store-Load ,

          即讀操作可能會重排到寫操作前面,同時不同線程的寫操作并沒有保證全局可見,例子見《Intel? 64 and IA-32 Architectures Software Developer’s Manual》手冊8.6.1、8.2.3.7節。

          要注意的是這個問題只能用mfence(或者是Lock前綴)解決,不能靠組合sfence和lfence解決。

          (用sfence+lfence組合僅可以解決重排問題,但不能解決全局可見性問題,簡單理解不如視為sfence和lfence本身也能亂序重拍)

         JMM的內存屏障分為四類:

          Java編譯器會在適當位置插入以下內存屏障來禁止重排序

          1. LoadLoad Barrier:相當于x86的 Load Barrier

          2. LoadStore Barrier:相當于x86的讀屏障+寫屏障的原子組合。

          3. StoreLoad Barrier:相當于x86的 Full Barrier(即寫屏障+讀屏障的原子組合)

          4. StoreStore Barrier:相當于x86的 Store Barrier

          

       

      4. Final

        如果final修飾一個基本數據類型,表示該基本數據類型的值一旦在初始化后便不能發生變化;

        如果final修飾一個引用類型,則在對其初始化之后便不能再讓其指向其他對象了,但該引用所指向的對象的內容是可以發生變化的。

        (其實本質上是一回事,因為引用的值是一個地址,final要求值,即地址的值不發生變化。)

        final修飾的屬性必須要初始化后才能返回這個類的實例對象,如果不初始化賦值會編譯失敗。可以在變量聲明的時候初始化 + 也可以在構造函數中對這個變量賦初值。

       

        Final的實現內存屏障:

        1. 對某個類的final域的初始化后,加入一個Store-load屏障,然后才能使用這個對象。(如果不加屏障就可能會重排序了,就會可能拿到的對象的final域是沒有初始化的)(盲猜也是Lock前綴,全屏障)

        2. (沒明白,可以不用說)初次讀這個對象的final域之前,加入一個load-Store屏障。(如果不加屏障就可能會重排序了,就會可能拿到的對象的final域是沒有初始化的)

       

        題外話,需要注意Final的使用方法: 

          Final的成員變量的注意事項:

           1. 

            該成員變量必須在創建對象之前進行賦值,否則編譯失敗。

            即定義成員變量的時候手動賦值 或者 利用構造器對成員變量進行賦值

           2. 

            final變量是屬于對象的,意思是同一個類的兩個對象的各自final變量可以是不相同的。

      5. 硬件層面需要解決兩個問題之一:緩存一致性問題:(CPU之間橫向層面,多核操作同一變量的問題)

        問題產生原因:CPU1緩存與CPU2緩存之間數據不同步的問題。

        解決方式1(不推薦):通過在總線加LOCK#鎖的方式(因為會變成單核CPU,不推薦)

        解決方式2(采用):MESI(緩存一致性協議)+Store Buffer(緩沖區)+Invalidate Queue

       

        MESI協議:

          Store Buffer & Invalidate Queue為MESI 提供了異步解決方案,強調的是異步。

       

          Store Buffer:讀寫時的更高級緩存

          Invalidate Queue:而當一個CPU核收到Invalid消息時,會把消息寫入自身的Invalidate Queue中,隨后異步將其設為Invalid狀態(具體什么時候未知)。

          Invalid狀態:非法

          Share狀態 :正在被共享

          Exclusive狀態:正在被獨占

          Modified狀態:緩存行已被修改,但是還未寫入主存。

       

          CPU向緩存寫數據時,

          先寫入Store-Buffer,

          如果該緩存行是Invalid,則從主存中獲取并刷新緩存先,再把自己緩存行設置為Share。

          如果該緩存行是Share ,就把其他CPU的這條緩存行都設置為Invalid,然后自己緩存行變為Exclusive。

          如果該緩存行是Exclusive,就把該緩存行設置為Modified,寫入緩存行,然后異步寫入主存(具體什么時候未知),然后把自己緩存行設置為Exclusive。

          如果該緩存行是Modified,就先等待異步寫入主存后(具體什么時候未知),再變為Exclusive先。

       

          CPU從緩存讀數據時,

          先掃描Store-Buffer,如果沒找到就去找緩存,

          如果該緩存行是Invalid,則從主存中獲取并刷新緩存,變為Share 。

          如果該緩存行是Share ,則直接讀。

          如果該緩存行是Exclusive ,則直接讀。

          如果該緩存行是Modified,需要等待變成Exclusive才可以讀。

       

          缺點1:當一個CPU核收到Invalid消息時,會把消息寫入自身的Invalidate Queue中,隨后異步將其設為Invalid狀態(具體什么時候未知)。這個期間可能就會發生讀取數據的操作,而此時的數據是臟數據。

          缺點2:當CPU向緩存寫數據時,在Modified之后&Exclusive之前,異步寫入主存(具體什么時候未知),而此時別人可能從主存讀取了臟數據。

       

      6. 硬件層面需要解決兩個問題之二: 指令重排問題:(時間縱向層面,多核操作同一變量的問題)

        問題原因之編譯器重排:

          由于編譯器只需要滿足JMM的as-if-serial規則,

          即,在單線程下不能改變結果。

          所以默認允許了指令重排序。

          但是會導致多線程下出現問題。

        問題原因之處理器重排:

          為了優化CPU運算效率,CPU在保證運算結果不變的情況下,允許指令重排。

          X86默認只允許Store-Load形式的指令重排。不允許Load-Load,Load-Store,Store-Load形式的指令重排。

          但是仍會導致多線程下出現問題。

       

        解決方式1:Volatile語義2,即內存屏障。(見上文)

          應用舉例:Volatile+DCL

       

        解決方式2:Final語義。(見上文)

       

        解決方式3:Synchronized(lock同理)

                      但是有局限性。

          synchronized(lock同理)只是把多線程執行一個塊(lock的trycatch塊)變為對于單線程執行一個塊(lock的trycatch塊)。

          synchronized(lock同理)保證的是線程1塊對線程2的同一個塊(lock的trycatch塊)不重排,但是塊內部的邏輯就不能保證不重排了。

          

        解決方式4:java自帶的happenbefore原則

       

      7. 硬件層面需要解決兩個問題之總結:

        MESI(CPU之間橫向層面,多核操作同一變量的問題,解決多核操作同一變量的問題)+內存屏障(時間縱向層面,解決多核操作同一變量的問題)組成了x86的解決方式。

        其中MESI是必然發生的,而內存屏障是可以我們手動加上的(Volatile)。

        Synchronized和Lock是JVM的解決方式

      8. JMM層面要求代碼需要滿足三個特性之一:原子性

        解決方式1:Synchronized修飾(lock同理)

          每一個synchronized塊(lock的trycatch塊),都是一整個原子操作。

        解決方式2:CAS

          cmpxchg一個指令完成了比較和交換兩個操作。

        解決方式3:AQS(內部使用了Volatile+CAS維護State的改變+同步隊列的節點狀態、節點前后、頭尾節點等)

      9. JMM層面要求代碼需要滿足三個特性之二:可見性

        解決方式1:Volatile

          Volatile語義1.(見上文)

        解決方式2:Synchronized(lock同理)

          但是有局限性。

          synchronized(lock同理)只是把多線程執行一個塊(lock的trycatch塊)變為對于單線程執行一個塊(lock的trycatch塊)

          synchronized(lock同理)保證的是線程1塊對線程2的同一個塊可見。不能保證塊內的第一行對第二行可見。

        解決方式3:final

          final語義(見上文)

        解決方式4:AQS(內部使用了Volatile+CAS維護State的改變+同步隊列的節點狀態、節點前后、頭尾節點等)

      10. JMM層面要求代碼需要滿足三個特性之三:有序性

        解決方式1:Volatile語義2,即內存屏障。(見上文)

          應用舉例:Volatile+DCL

        解決方式2:Final語義。(見上文)

        解決方式3:Synchronized(lock同理)

                      但是有局限性。

          synchronized(lock同理)只是把多線程執行一個塊(lock的trycatch塊)變為對于單線程執行一個塊(lock的trycatch塊)。

          synchronized(lock同理)保證的是線程1塊對線程2的同一個塊(lock的trycatch塊)不重排,但是塊內部的邏輯就不能保證不重排了。

        解決方式4:java自帶的happens-before原則

        解決方式5:AQS(內部使用了Volatile+CAS維護State的改變+同步隊列的節點狀態、節點前后、頭尾節點等)

       

      11. Happens-Before規則: 

        為了實現有序性,我們可以使用Volatile或Synchronized或final,但是這樣對程序員不友好,因為代碼會很煩瑣。為了輔助Volatile或Synchronized或final,所以Java語言自帶了Happens-Before規則:。

       

        Happens-Before規則的意思是,java天生自帶了某些情況下的兩個操作的前后可見,

        即假如A happens- before B,則A對于B是可見的。但是并不表示A必須在B之前執行。

        意思是,可以重排,但是不能影響我們兩者的有序性,所以其實也一定程度地約束了不允許重排。

        至于Java是怎么保證AB有序性的(或者叫A對于B的可見性),盲猜是使用了JMM的四種內存屏障吧。

       

        程序順序規則:在一個線程內,

          前:前面的操作,后:后面的操作。

        Synchronize規則:對于一個monitor鎖,

          前:unlock,后:lock。

        Volatile規則:對于一個 volatile變量,

          前:寫,后:讀。

        線程啟動規則:對于一個Thread對象,

          前:Thread.start,后:Thread的內部方法被調用。

        線程中斷規則:對于一個Thread對象,

          前:Thread.interrupt,后:interrupt被檢測到

        線程終止規則:對于一個Thread對象t1,還有一個線程t2在t1內部運行t2.join,

          前:t2的所有操作,后:t2.join結束后,t1恢復

        對象終結原則:對于一個對象,

          前:對象初始化,后:對象執行finalize

        傳遞性:對于操作A先于操作B,并且,操作B先于操作C,則操作A先于操作C

        

       

       

      posted on 2020-08-10 20:44  Optimus_7  閱讀(749)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国内在线视频一区二区三区| 少妇激情一区二区三区视频小说| 国产欧美综合在线观看第十页| 国产精品国色综合久久| 欧美黑人巨大xxxxx| 欧美成人午夜在线观看视频| 国产麻豆成人精品av| 国产精品美女一区二三区| 在线精品国产成人综合| 2021AV在线无码最新| 无码人妻精品一区二区三区下载 | 日韩亚洲欧美中文高清| 亚洲一区成人在线视频| 加查县| 亚洲中文字幕无码一久久区| 免费看黄色片| 成人免费A级毛片无码网站入口| 亚洲日本中文字幕天天更新| 成人啪精品视频网站午夜| 韩日午夜在线资源一区二区| 日本中文一二区有码在线| 日韩一区二区三区亚洲一| 国内不卡一区二区三区| 亚洲AV无码秘?蜜桃蘑菇| 风韵丰满熟妇啪啪区老老熟妇| 日本一区二区三区四区黄色| 人妻丝袜无码专区视频网站 | 亚洲国产日韩A在线亚洲| 富民县| 中文字幕乱妇无码av在线 | 国产又大又粗又爽的毛片| 精品国产一区二区三区蜜臀| 隔壁老王国产在线精品| 综合欧美视频一区二区三区| 国产午夜精品久久一二区| 久久久天堂国产精品女人| 大尺度国产一区二区视频| 一本无码人妻在中文字幕免费| 国产极品美女高潮无套| 香蕉EEWW99国产精选免费| 国产成人一区二区三区视频免费|