真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java并發(fā)編程實(shí)戰(zhàn)學(xué)習(xí)筆記Day7-創(chuàng)新互聯(lián)

并發(fā)編程是比較進(jìn)階的知識,涉及到很多底層的東西,學(xué)習(xí)起來是比較困難的。并發(fā)編程的bug更多的是偶發(fā)性的,很難復(fù)現(xiàn),排查起來也很困難,要想快速解決問題,就要理解并發(fā)編程的本質(zhì),追本溯源,深入分析bug的源頭。

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

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

我們都知道CPU、內(nèi)存、I/O設(shè)備三者的速度差異,程序在運(yùn)行過程中,大部分都要訪問內(nèi)存,有些也要訪問I/O,程序的整體性能取決于最慢的I/O設(shè)備,為了合理利用CPU的高性能,平衡三者的速度差異,計(jì)算機(jī)體系結(jié)構(gòu)、操作系統(tǒng)、編譯程序都有貢獻(xiàn)

  • CPU增加了緩存,以平衡和內(nèi)存的速度差異;
  • 操作系統(tǒng)增加了進(jìn)程、線程,來分時(shí)復(fù)用CPU,進(jìn)而均衡CPU與I/O設(shè)備的速度差異;
  • 編譯程序優(yōu)化指令執(zhí)行次序,使得緩存能夠得到更加合理利用
源頭之一:緩存導(dǎo)致的可見性問題

單核時(shí)代,所有的線程都是在一顆CPU上執(zhí)行,CPU緩存和內(nèi)存的數(shù)據(jù)一致性很容易保證,因?yàn)樗械木€程操作的都是同一個(gè)CPU緩存,一個(gè)線程對緩存的操在對其他線程來說是一定可見的。

多核時(shí)代,每顆CPU都有自己的緩存,這是CPU緩存與內(nèi)存的數(shù)據(jù)一致性就不容易解決了,當(dāng)多個(gè)線程在不同的CPU上執(zhí)行時(shí),這些線程操作的是不同的CPU緩存。

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

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

  • 1.首先,需要把變量count從內(nèi)存加載到CPU寄存器
  • 2.之后,在寄存器中執(zhí)行+1操作
  • 3.將結(jié)構(gòu)寫入內(nèi)存。

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

image

造成這個(gè)問題的原因是因?yàn)槲覀円詾閏ount+=1這個(gè)操作是一個(gè)整體,其實(shí)再多線程執(zhí)行的時(shí)候,線程的切換會(huì)發(fā)生在count+=1之前也可能是之后,就不會(huì)發(fā)生在中間。我們把一個(gè)或多個(gè)操作在CPU執(zhí)行的過程中不被中斷的特性稱為原子性。

源頭三:編譯優(yōu)化帶來的有序性問題

編譯器在百衲衣程序的時(shí)候,為了優(yōu)化性能,會(huì)改變語句中的先后順序,絕大部分情況下都是沒有問題的,不會(huì)影響程序的最終結(jié)果,不過有時(shí)候編譯器及解釋器優(yōu)化導(dǎo)致的bug也是改到頭皮發(fā)麻。

以Java中的雙重檢查創(chuàng)建單例對象代碼為例:

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

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

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

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

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

實(shí)際經(jīng)過編譯器優(yōu)化之后的執(zhí)行路徑可能是:1-3-2

優(yōu)化之后,假設(shè)線程A執(zhí)行到第2步的是,發(fā)生了線程切換,切換到線程B,線程B也執(zhí)行g(shù)etInstance()方法,那么線程B執(zhí)行,發(fā)現(xiàn)instance != null,直接返回使用,此時(shí)instance還未進(jìn)行初始化,此時(shí)就可能發(fā)生空指針異常。

學(xué)好并發(fā)編程,就要深刻理解可見性、原子性、有序性在并發(fā)場景下的原理,在面對很多問題的時(shí)候就會(huì)迎刃而解。

學(xué)習(xí)來源:極客時(shí)間

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧


當(dāng)前標(biāo)題:Java并發(fā)編程實(shí)戰(zhàn)學(xué)習(xí)筆記Day7-創(chuàng)新互聯(lián)
標(biāo)題路徑:http://weahome.cn/article/cdedsp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部