日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

了解这些坑,再也不会出现诡异的BUG了~

發(fā)布時間:2025/3/16 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 了解这些坑,再也不会出现诡异的BUG了~ 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

在高并發(fā)的情況下,你的程序是不是經(jīng)常出現(xiàn)一些詭異的BUG,每次都是花費大量時間排查,但是你有沒有思考過這一切罪惡的源頭是什么呢?

?

幕后那些事

CPU、內(nèi)存、I/O設備的速度差異越來越大,這也是程序性能的瓶頸,根據(jù)木桶理論,最終決定程序的整體性能取決于最慢的操作-讀寫I/O設備,單方面的提高CPU的性能是無用的。

為了平衡三者的差距,大牛前輩們不斷努力,最終做出了卓越的貢獻:

  • CPU增加了緩存,平衡與內(nèi)存之間的速度差異

  • 操作系統(tǒng)增加了進程、線程,以分時復用 CPU,進而均衡 CPU 與 I/O 設備的速度差異;

  • 編譯程序優(yōu)化指令執(zhí)行次序,使得緩存能夠得到更加合理地利用。

  • 注意:正是硬件前輩們做的這些貢獻,額外的后果需要軟件工程師來承擔,太坑了。

    ?

    坑一:CPU緩存導致的可見性問題

    在單核CPU的時代,所有的線程都在單個CPU上執(zhí)行,不存在CPU數(shù)據(jù)和內(nèi)存的數(shù)據(jù)的一致性。

    一個線程對共享變量的修改,另外一個線程能夠立刻看到,我們稱為可見性。

    因為所有的線程都是在同一個CPU緩存中讀寫數(shù)據(jù),一個線程對緩存的寫,對于另外一個線程肯定是可見的。如下圖:

    單核CPU與內(nèi)存關系

    從上圖可以很清楚的了解,線程A對于變量的修改都是在同一個CPU緩存中,則線程B肯定是可見的。

    但是多核時代的到來則意味著每個CPU上都有一個獨立的緩存,信息不再互通了,此時保證內(nèi)存和CPU緩存的一致性就很難了。如下圖:

    雙核CPU與內(nèi)存關系

    從上圖可以很清楚的了解,線程A和線程B對變量A的改變是不可見的,因為是在兩個不同的CPU緩存中。

    最簡單的證明方式則是在多核CPU的電腦上跑一個循環(huán)相加的方法,同時開啟兩個線程運行,最終得到的結果肯定不是正確的,如下:

    public?class?TestThread?{private?Long?total=0L;//循環(huán)一萬次相加private?void?add(){for?(int?i?=?0;?i?<?10000;?i++)?{total+=1;}}//開啟兩個線程相加public?static?void?calc()?throws?InterruptedException?{TestThread?thread=new?TestThread();//創(chuàng)建兩個線程Thread?thread1=new?Thread(thread::add);Thread?thread2=new?Thread(thread::add);//啟動線程thread1.start();thread2.start();//阻塞主線程thread1.join();thread2.join();System.out.println(thread.total);}

    上述代碼在單核CPU的電腦上運行的結果肯定是20000,但是在多核CPU的電腦上運行的結果則是在10000~20000之間,為什么呢?

    原因很簡單,第一次在兩個線程啟動后,會將total=0讀取到各自的CPU緩存中,執(zhí)行total+1=0后,各自將得到的結果total=1寫入到內(nèi)存中(理想中應該是total=2),由于各自的CPU緩存中都有了值,因此每個線程都是基于各自CPU緩存中的值來計算,因此最終導致了寫入內(nèi)存中的值是在10000~20000之間。

    注意:如果循環(huán)的次數(shù)很少,這種情況不是很明顯,如果次數(shù)設置的越大,則結果越明顯,因為兩個線程不是同時啟動的。

    ?

    坑二:線程切換導致的原子性問題

    早期的操作系統(tǒng)是基于進程調(diào)度CPU,不同進程間是共享內(nèi)存空間的,比如你在IDEA寫代碼的同時,能夠打開QQ音樂,這個就是多進程。

    操作系統(tǒng)允許某個進程執(zhí)行一段時間,比如40毫秒,過了這個時間則會選擇另外一個進程,這個過程稱之為任務切換,這個40毫秒稱之為時間片,如下圖:

    任務切換

    在一個時間片內(nèi),如果一個進程進行IO操作,比如讀文件,這個時候該進程可以把自己標記為休眠狀態(tài)并讓出CPU的使用權,待文件讀進內(nèi)存,操作系統(tǒng)會將這個休眠的進程喚醒,喚醒后的進程就有機會重新獲得CPU的使用權。

    現(xiàn)代的操作系統(tǒng)更加輕量級了,都是基于線程調(diào)度,現(xiàn)在提到的任務切換大都指示線程切換。

    注意:操作系統(tǒng)進行任務切換是基于CPU指令。

    基于CPU指令是什么意思呢?Java作為高級編程語言,一條簡單的語句可能底層就需要多條CPU指令,例如total+=1這條語句,至少需要三條CPU指令,如下:

  • 指令1:將total從內(nèi)存讀到CPU寄存器中

  • 指令2:在寄存器中執(zhí)行+1

  • 指令3:將結果寫入內(nèi)存(緩存機制可能導致寫入的是CPU緩存而不是內(nèi)存)

  • 基于CPU指令是什么意思呢?簡單的說就是任務切換的時機可能是上面的任何一條指令完成之后。

    我們假設在線程A執(zhí)行了指令1后做了任務切換,此時線程B執(zhí)行,雖然執(zhí)行了total+=1,但是最終的結果卻不是2,如下圖:

    非原子操作

    我們把一個或者多個操作在CPU執(zhí)行過程中不被中斷的特性稱之為原子性。

    注意:CPU僅僅能保證CPU指令執(zhí)行的原子性,并不能保證高級語言的單條語句的原子性。

    此處分享一道經(jīng)典的面試題:Long類型的數(shù)據(jù)在32位操作系統(tǒng)中加減是否存在并發(fā)問題?答案:是,因為Long類型是64位,在32位的操作系統(tǒng)中執(zhí)行加減肯定是要拆分成多個CPU指令,因此無法保證加減的原子性。

    ?

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

    編譯優(yōu)化算是最詭異的一個難題了,雖然高級語言規(guī)定了代碼的執(zhí)行順序,但是編譯器有時為了優(yōu)化性能,則會改變代碼執(zhí)行的順序,比如a=4;b=3;,在代碼中可能給人直觀的感受是a=4先執(zhí)行,b=3后執(zhí)行,但是編譯器可能為了優(yōu)化性能,先執(zhí)行了b=3,這種對于我們?nèi)庋凼遣豢梢姷?#xff0c;上面例子中雖然不影響結果,但是有時候編譯器的優(yōu)化可能導致意想不到的BUG。

    雙重校驗鎖實現(xiàn)單例不知大家有沒有聽說過,代碼如下:

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

    這里我去掉了volatile關鍵字,那么此時這個代碼在并發(fā)的情況下有問題嗎?

    上述代碼看上去很完美,但是最大的問題就在new Singleton();這行代碼上,預期中的new操作順序如下:

  • 分配一塊內(nèi)存N

  • 在內(nèi)存N上初始化Singleton對象

  • 將內(nèi)存N的地址賦值給instance變量

  • 但是實際上編譯優(yōu)化后的執(zhí)行順序如下:

  • 分配一塊內(nèi)存N

  • 將內(nèi)存N的地址賦值給instance變量

  • 在內(nèi)存N上初始化Singleton對象

  • 很多人問了,優(yōu)化后影響了什么?

    將內(nèi)存N的地址提前賦值給instance變量意味著instance!=null是成立的,一旦是高并發(fā)的情況下,線程A執(zhí)行第二步發(fā)生了任務切換,則線程B執(zhí)行到了if (instance == null)這個判斷,此時不成立,則直接返回了instance,但是此時的instance并沒有初始化過,如果此時訪問其中的成員變量則會發(fā)生空指針異常,執(zhí)行流程如下圖:

    單例NPE

    ?

    總結

    并發(fā)編程是區(qū)分高低手的門檻,只有深刻理解三大特性:可見性、原子性、有序性才能解決詭異的BUG。

    本文分析了帶來這三大特性源頭,如下:

  • CPU緩存導致的可見性問題

  • 線程切換帶來的原子性問題

  • 編譯優(yōu)化帶來的有序性問題

  • 有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!

    總結

    以上是生活随笔為你收集整理的了解这些坑,再也不会出现诡异的BUG了~的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 鬼灭之刃柱训练篇在线观看 | 日韩极品视频 | 日韩美一区二区 | 欧美性色19p| 粗喘呻吟撞击猛烈疯狂 | 嫩草研究院在线观看 | 在线亚洲精品 | 中文字幕国产亚洲 | 奇米第四色7777 | 91av免费在线观看 | 高潮一区二区 | 4438国产精品一区二区 | 丰满双乳秘书被老板狂揉捏 | 91涩漫成人官网入口 | 天堂av资源在线 | 日韩视频一区在线观看 | 青春草国产视频 | 久久久久亚洲无码 | 日本国产精品一区 | 老司机免费在线视频 | 超能一家人电影免费喜剧在线观看 | 神宫寺奈绪一区二区三区 | 精品中文字幕在线观看 | 天堂av日韩 | 97精品一区二区视频在线观看 | 日韩在线免费看 | 亚洲免费色图 | 亚洲自拍小视频 | 一级片aaaaa| 亚洲中文字幕一区 | 久久免费视频一区二区 | 男女ss视频| 亚洲免费激情视频 | 欧美视频在线观看 | 日韩在线精品强乱中文字幕 | 影音先锋亚洲资源 | 久久精品色妇熟妇丰满人妻 | 国产真实乱人偷精品视频 | 女色婷婷| www日本免费| 欧美性视频一区二区 | 国产男女啪啪 | 亚洲日本激情 | www.九九热 | 久久成人亚洲 | 日本免费一区二区三区四区五六区 | 另类小说婷婷 | 黄色女女| 超碰碰97 | av无码一区二区三区 | 日本a在线免费观看 | 色网站免费观看 | va婷婷 | 日本免费精品 | 3d动漫啪啪精品一区二区中文字幕 | re久久| 免费色片网站 | 亚洲精品视频在线看 | 中文字幕 人妻熟女 | 精品少妇v888av | 亚洲精品日韩综合观看成人91 | 青青草原国产在线 | 免费操片 | 91精品国产乱码久久久张津瑜 | 秋霞一级视频 | 4438x五月天 日吊视频 | 欧美精品久久天天躁 | 九一精品视频 | 亚洲一二三不卡 | 日韩八区 | 91免费国产在线 | 日本成人片在线 | 免费观看日批视频 | 黄色av免费在线 | 国产强被迫伦姧在线观看无码 | 超碰在线免费看 | 欧美乱色| 6699嫩草久久久精品影院 | 日日操夜夜操狠狠操 | www麻豆 | 色婷婷av777 麻豆传媒网站 | 在线播放黄色网址 | 久久久久99精品成人片我成大片 | 国产在线一区二区三区 | 欧美日韩系列 | 国产成人精品白浆久久69 | 欧美xxxxbbb| 男女午夜影院 | 国产a毛片 | 亚洲视频 一区 | 免费性网站 | 亚洲一级理论片 | 影音先锋亚洲天堂 | 久久夜靖品2区 | 欧美性受xxxx黑人猛交88 | 国产人妖一区二区 | 亚洲成a人在线观看 | 中日韩av在线 | 国产a久久麻豆入口 |