Java面試之多執行緒&併發篇(9)

码路编程發表於2024-11-29

前言

本來想著給自己放鬆一下,刷刷部落格,突然被幾道面試題難倒!引用型別有哪些?有什麼區別?說說你對JMM記憶體模型的理解?為什麼需要JMM?多執行緒有什麼用?似乎有點模糊了,那就大概看一下面試題吧。好記性不如爛鍵盤

*** 12萬字的java面試題整理 ***
*** java核心面試知識整理 ***
*** Java高頻面試講解影片(知識涵蓋齊全) ***

說說你對JMM記憶體模型的理解?為什麼需要JMM?

隨著CPU和記憶體的發展速度差異的問題,導致CPU的速度遠快於記憶體,所以現在的CPU加入了快取記憶體,快取記憶體一般可以分為L1、L2、L3三級快取。基於上面的例子我們知道了這導致了快取一致性的問題,所以加入了快取一致性協議,同時導致了記憶體可見性的問題,而編譯器和CPU的重排序導致了原子性和有序性的問題,JMM記憶體模型正是對多執行緒操作下的一系列規範約束,因為不可能讓陳僱員的程式碼去相容所有的CPU,透過JMM我們才遮蔽了不同硬體和作業系統記憶體的訪問差異,這樣保證了Java程式在不同的平臺下達到一致的記憶體訪問效果,同時也是保證在高效併發的時候程式能夠正確執行。

  • 原子性:Java記憶體模型透過read、load、assign、use、store、write來保證原子性操作,此外還有lock和unlock,直接對應著synchronized關鍵字的monitorenter和monitorexit位元組碼指令。
  • 可見性:可見性的問題在上面的回答已經說過,Java保證可見性可以認為透過volatile、synchronized、final來實現。
  • 有序性:由於處理器和編譯器的重排序導致的有序性問題,Java透過volatile、synchronized來保證。

happen-before規則

雖然指令重排提高了併發的效能,但是Java虛擬機器會對指令重排做出一些規則限制,並不能讓所有的指令都隨意的改變執行位置,主要有以下幾點:

  1. 單執行緒每個操作,happen-before於該執行緒中任意後續操作
  2. volatile寫happen-before與後續對這個變數的讀
  3. synchronized解鎖happen-before後續對這個鎖的加鎖
  4. final變數的寫happen-before於final域物件的讀,happen-before後續對final變數的讀
  5. 傳遞性規則,A先於B,B先於C,那麼A一定先於C發生

說了半天,到底工作記憶體和主記憶體是什麼?

主記憶體可以認為就是實體記憶體,Java記憶體模型中實際就是虛擬機器記憶體的一部分。而工作記憶體就是CPU快取,他有可能是暫存器也有可能是L1\L2\L3快取,都是有可能的。

多執行緒有什麼用?

一個可能在很多人看來很扯淡的一個問題:我會用多執行緒就好了,還管它有什麼用?在我看來,這個回答更扯淡。所謂"知其然知其所以然","會用"只是"知其然","為什麼用"才是"知其所以然",只有達到"知其然知其所以然"的程度才可以說是把一個知識點運用自如。OK,下面說說我對這個問題的看法:
(1)發揮多核CPU的優勢
隨著工業的進步,現在的筆記本、桌上型電腦乃至商用的應用伺服器至少也都是雙核的,4核、8核甚至16核的也都不少見,如果是單執行緒的程式,那麼在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。單核CPU上所謂的"多執行緒"那是假的多執行緒,同一時間處理器只會處理一段邏輯,只不過執行緒之間切換得比較快,看著像多個執行緒"同時"執行罷了。多核CPU上的多執行緒才是真正的多執行緒,它能讓你的多段邏輯同時工作,多執行緒,可以真正發揮出多核CPU的優勢來,達到充分利用CPU的目的。
(2)防止阻塞
從程式執行效率的角度來看,單核CPU不但不會發揮出多執行緒的優勢,反而會因為在單核CPU上執行多執行緒導致執行緒上下文的切換,而降低程式整體的效率。但是單核CPU我們還是要應用多執行緒,就是為了防止阻塞。試想,如果單核CPU使用單執行緒,那麼只要這個執行緒阻塞了,比方說遠端讀取某個資料吧,對端遲遲未返回又沒有設定超時時間,那麼你的整個程式在資料返回回來之前就停止執行了。多執行緒可以防止這個問題,多條執行緒同時執行,哪怕一條執行緒的程式碼執行讀取資料阻塞,也不會影響其它任務的執行。
(3)便於建模
這是另外一個沒有這麼明顯的優點了。假設有一個大的任務A,單執行緒程式設計,那麼就要考慮很多,建立整個程式模型比較麻煩。但是如果把這個大的任務A分解成幾個小任務,任務B、任務C、任務D,分別建立程式模型,並透過多執行緒分別執行這幾個任務,那就簡單很多了。

相關文章