bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

Java并發編程實戰學習筆記Day7-創新互聯

并發編程是比較進階的知識,涉及到很多底層的東西,學習起來是比較困難的。并發編程的bug更多的是偶發性的,很難復現,排查起來也很困難,要想快速解決問題,就要理解并發編程的本質,追本溯源,深入分析bug的源頭。

成都創新互聯于2013年成立,是專業互聯網技術服務公司,擁有項目成都網站制作、網站建設、外貿網站建設網站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元原平做網站,已為上家服務,為原平各地企業和個人服務,聯系電話:18980820575

從這篇文章開始,我將記錄我學習Java并發編程的所有筆記,這是第一篇,希望可以幫助到更多需要的朋友。

我們都知道CPU、內存、I/O設備三者的速度差異,程序在運行過程中,大部分都要訪問內存,有些也要訪問I/O,程序的整體性能取決于最慢的I/O設備,為了合理利用CPU的高性能,平衡三者的速度差異,計算機體系結構、操作系統、編譯程序都有貢獻

  • CPU增加了緩存,以平衡和內存的速度差異;
  • 操作系統增加了進程、線程,來分時復用CPU,進而均衡CPU與I/O設備的速度差異;
  • 編譯程序優化指令執行次序,使得緩存能夠得到更加合理利用
源頭之一:緩存導致的可見性問題

單核時代,所有的線程都是在一顆CPU上執行,CPU緩存和內存的數據一致性很容易保證,因為所有的線程操作的都是同一個CPU緩存,一個線程對緩存的操在對其他線程來說是一定可見的。

多核時代,每顆CPU都有自己的緩存,這是CPU緩存與內存的數據一致性就不容易解決了,當多個線程在不同的CPU上執行時,這些線程操作的是不同的CPU緩存。

源頭二:線程切換帶來的原子性問題

Java并發編程都是基于多線程的,自然也會涉及到任務切換。任務切換的時機大多數都是在時間片結束的時候,我們現在基本都使用高級語言編程,高級語言里一條語句往往需要多條CPU指令完成,例如代碼:count += 1,至少三條CPU指令。

  • 1.首先,需要把變量count從內存加載到CPU寄存器
  • 2.之后,在寄存器中執行+1操作
  • 3.將結構寫入內存。

操作系統做任務切換,可以發生在任何一條CPU指令執行完,對于上面的三條指令來說,我們假設count=0,如果線程A在指令1執行完成后線程切換,線程B開始執行,線程B把三條指令都執行完成,再切換回線程A繼續執行,此時線程A執行指令2,3,最終得到的結果還是1,而不是我們期望的2

image

造成這個問題的原因是因為我們以為count+=1這個操作是一個整體,其實再多線程執行的時候,線程的切換會發生在count+=1之前也可能是之后,就不會發生在中間。我們把一個或多個操作在CPU執行的過程中不被中斷的特性稱為原子性。

源頭三:編譯優化帶來的有序性問題

編譯器在百衲衣程序的時候,為了優化性能,會改變語句中的先后順序,絕大部分情況下都是沒有問題的,不會影響程序的最終結果,不過有時候編譯器及解釋器優化導致的bug也是改到頭皮發麻。

以Java中的雙重檢查創建單例對象代碼為例:

public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
        }
    }
    return instance;
  }
}

在獲取實例 getInstance() 的方法中,我們首先判斷 instance 是否為空,如果為空,則鎖定 Singleton.class 并再次檢查 instance 是否為空,如果還為空則創建 Singleton 的一個實例。

假設同時兩個線程A、B同時調用getInstance()方法,此時instance==null,于是對Singleton.class加鎖,此時,JVM保證只有一個線程能夠加鎖成功,另外一個線程則會等待(假設是線程 B);線程 A 會創建一個 Singleton 實例,之后釋放鎖,鎖釋放后,線程 B 被喚醒,線程 B 再次嘗試加鎖,此時是可以加鎖成功的,加鎖成功后,線程 B 檢查 instance == null 時會發現,已經創建過 Singleton 實例了,所以線程 B 不會再創建一個 Singleton 實例。

看上去問題不大,實際上還是存在問題的,問題就出在new操作不是原子的,我們以為的new操作:

  1. 分配一塊內存M
  2. 在內存M上初始化Singleton對象
  3. 將M的地址復制給instance變量

實際經過編譯器優化之后的執行路徑可能是:1-3-2

優化之后,假設線程A執行到第2步的是,發生了線程切換,切換到線程B,線程B也執行getInstance()方法,那么線程B執行,發現instance != null,直接返回使用,此時instance還未進行初始化,此時就可能發生空指針異常。

學好并發編程,就要深刻理解可見性、原子性、有序性在并發場景下的原理,在面對很多問題的時候就會迎刃而解。

學習來源:極客時間

你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

文章標題:Java并發編程實戰學習筆記Day7-創新互聯
網頁URL:http://vcdvsql.cn/article38/cdedsp.html

成都網站建設公司_創新互聯,為您提供網站設計公司全網營銷推廣網站內鏈網站排名ChatGPT域名注冊

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

成都網站建設