今天主要就是介紹一下 CPU 中的多級緩存和亂序執行優化,爲後面學習多線程作鋪墊。先來理解一下 CPU 的結構,後面再說 Java 虛擬機的內存模型。編程
先放兩張圖看一下 CPU 和各級緩存、內存、硬盤之間的關係。緩存
下面就來介紹一下爲何會出現多級緩存,以及會出現什麼問題,CPU 又是如何解決的。多線程
爲何會出現多級緩存呢?說的簡單一點由於 CPU 的頻率太快了,而如果沒有緩存,直接讀取內存中的數據又太慢了,咱們不想讓 CPU 停下來等待,因此加入了一層讀取速度大於內存但小於 CPU 的這麼一層東西,這就是緩存。架構
加入緩存以後,CPU 須要數據就問緩存要,緩存沒有就從主存中讀取,並保留一份在緩存中。下次讀取就從緩存中讀取,加快速度。編程語言
可是,咱們常常聽到 CPU 運行速度快,緩存次之,而內存慢一點,硬盤最慢。這又是爲何呢?性能
CPU 運行的快,那是由於一個時鐘週期短,一個時鐘週期是指機器碼0和1變化,實質就是電信號一高一低之間所用的時間,這可都是電信號啊,電的速度能不快嘛!也就 10 納秒左右吧,1 秒等於 10 的 9 次方納秒。學習
下面介紹一下緩存和內存優化
目前緩存基本上都是採用 SRAM 存儲器,SRAM 是英文 Static RAM 的縮寫,它是一種具備靜志存取功能的存儲器,不須要刷新電路即能保存它內部存儲的數據。不像 DRAM 內存那樣須要刷新電路,每隔一段時間,固定要對 DRAM 刷新充電一次,不然內部的數據即會消失,所以 SRAM 具備較高的性能,可是 SRAM 也有它的缺點,即它的集成度較低,相同容量的 DRAM 內存能夠設計爲較小的體積,可是 SRAM 卻須要很大的體積,這也是目前不能將緩存容量作得太大的重要緣由。這中間也解釋了爲何內存中的數據一斷電就沒有了。線程
而 RAM(隨機讀寫存儲器)的工做原理大體是當 CPU 讀取主存時,將地址信號放到地址總線上傳給主存,主存讀到地址信號後,解析信號並定位到指定存儲單元,而後將此存儲單元數據放到數據總線上返回給 CPU。設計
磁盤慢就慢在它的讀取是須要藉助於磁頭移動,這有尋址的過程,而這仍是一個機械運動的過程,上面慢也是在和電信號打交道,而磁盤不只須要電還須要擺臂,因此,最慢的就是它了。
再說一點,咱們說的主存就是咱們常說的內存,比方說個人電腦的內存是 12 G,磁盤的空間是 1 T。
整理一下就是,咱們爲了提升 CPU 的利用率,添加了多級緩存,可是呢,數據的讀取和保存都要在主存上進行,如果單線程是沒有問題的,一條路走下去,該讀讀該寫寫。
可是在多線程的狀況下就會出現問題,由於每一個線程都有本身的緩存,假如線程 1 從主存中讀取到 x,並對其加 1 ,此時尚未寫回主存,線程 2 也從主存中讀取 x ,並加 1 ,它們是不知道對方的,也不能夠讀取對方的緩存。這時都將 x 寫回主存,那此時 x 的值就少了 1 。
這也就是多線程狀況下帶有緩存的問題,數據出現問題了,怎麼辦呢?不能把緩存去掉吧,這時就須要一種協議,來保證不一樣的線程在讀寫主存數據時遵照某種規則以保證不會出現數據不一致的問題。這種協議有不少,其中用的比較多的是 MESI 協議,主要用來保證緩存的一致性。
MESI 爲了保證多個緩存中共享數據的一致性,定義了 cache line 的四種狀態,而線程對 cache line 的四種操做可能會產生不一致的狀態,所以緩存控制器監聽到本地操做和遠程操做的時候,須要對地址一致的 cache line 狀態進行一致性修改,從而保證數據在多個緩存之間保持一致性。(M: modified E: Exclusive S: shared I: invalid)
CPU 中每一個緩存行(caceh line)使用 4 種狀態進行標記(使用額外的兩位(bit)表示)。
M: 被修改(Modified)
該緩存行只被緩存在該 CPU 的緩存中,而且是被修改過的(dirty),即與主存中的數據不一致,該緩存行中的內存須要在將來的某個時間點(容許其它 CPU 讀取請主存中相應內存以前)寫回主存。當被寫回主存以後,該緩存行的狀態會變成獨享(exclusive)狀態。
E: 獨享的(Exclusive)
該緩存行只被緩存在該 CPU 的緩存中,它是未被修改過的(clean),與主存中數據一致。該狀態能夠在任什麼時候刻當有其它 CPU 讀取該內存時變成共享狀態(shared)。一樣地,當 CPU 修改該緩存行中內容時,該狀態能夠變成 Modified 狀態。
S: 共享的(Shared)
該狀態意味着該緩存行可能被多個 CPU 緩存,而且各個緩存中的數據與主存數據一致(clean),當有一個 CPU 修改該緩存行中,其它 CPU 中該緩存行能夠被做廢(變成無效狀態)。
I: 無效的(Invalid)
該緩存是無效的(可能有其它 CPU 修改了該緩存行)。
cache line 不一樣的狀態之間能夠相互轉化,這也就是 MESI 協議的具體內容,比方說一個處於 M 狀態的緩存行必須時刻監聽全部試圖讀該緩存行相對應主存的操做,這種操做必須在緩存將該緩存行寫回主存並將狀態變成 S 狀態以前被延遲執行。
還有不少,就不一一說了。
再回頭看看那個圖是否是清晰多了,在 MESI 出現以前的解決緩存一致性的方案是總線鎖機制,這種解決方案效率很低,鎖住總線期間,其餘 CPU 沒法訪問內存。
說完了 CPU 中的多級緩存,再來看看 CPU 中的亂序執行優化,什麼意思呢,處理器爲提升運算速度而作出違背代碼原有順序的優化。雖然順序變了,可是執行的結果是不會變的。
舉個例子,我要去買杯飲料喝,正常邏輯是這樣的,去店裏->點單->付錢->喝飲料。那其實我還能夠在去店裏的路上同時就把【點單付錢】一塊兒搞定,又或者我先【點單】再去店裏付錢拿飲料。
類比到 CPU 中也是這樣,求 x + y = ? 我計算 x 的值,再計算 y 的值,其實能夠一塊兒執行,或是先計算 y 的值,固然爲了提升運算速度,它會同時計算 x 和 y 的值,一個 CPU 中又不是隻有一個邏輯計算的單元。
亂序優化就是這樣,爲了提升效率 CPU 作出的優化,這在單核的時候是不會有問題的,可是在多核時代又會出現問題。
再舉個例子,線程 1 須要借組一個 flag 變量執行一段邏輯,你能用 C線程 2 來亂序優化一下,先執行邏輯而不看 flag 變量的值?這是確定不行的。
行仍是不行確定有一套規則,這套規則也就是內存屏障,過高級了聽不懂,換一種說法,就是說不一樣架構的處理器在其指令集中提供了不一樣的指令來發起內存屏障,對應在編程語言當中就是提供特殊的關鍵字來調用處理器相關的指令。結果就是有些指令能亂有些則不能!
好了,到這裏就算把 CPU 級別的特性給說完了,這些知識對於咱們理解 Java 內存模型(JMM) 以及多線程編程頗有用。而 Java 內存模型就是借鑑了硬件的結構。
總結一下,在 CPU 中爲了提升運行效率,加了多級緩存和亂序執行優化。加了多級緩存以後呢,會出現緩存不一致的狀況,解決的辦法就是定義了 MESI 等相似協議。對於亂序執行優化帶來的問題,CPU 選擇內存屏障來解決,即定義了一套指令集,什麼樣的指令不能執行亂序優化。
以上也就是介紹完了硬件的內存架構,看完了 CPU 中的騷操做,是否是火燒眉毛的想要看 Java 中的內存模型了,不要走開,下節更精彩!