微網(wǎng)站 微官網(wǎng)的區(qū)別嗎最近的新聞大事10條
前言
本來想著給自己放松一下,刷刷博客,突然被幾道面試題難倒!引用類型有哪些?有什么區(qū)別?說說你對JMM內存模型的理解?為什么需要JMM?多線程有什么用?似乎有點模糊了,那就大概看一下面試題吧。好記性不如爛鍵盤
*** 12萬字的java面試題整理 ***
*** java核心面試知識整理 ***
*** Java高頻面試講解視頻(知識涵蓋齊全) ***
說說你對JMM內存模型的理解?為什么需要JMM?
隨著CPU和內存的發(fā)展速度差異的問題,導致CPU的速度遠快于內存,所以現(xiàn)在的CPU加入了高速緩存,高速緩存一般可以分為L1、L2、L3三級緩存。基于上面的例子我們知道了這導致了緩存一致性的問題,所以加入了緩存一致性協(xié)議,同時導致了內存可見性的問題,而編譯器和CPU的重排序導致了原子性和有序性的問題,JMM內存模型正是對多線程操作下的一系列規(guī)范約束,因為不可能讓陳雇員的代碼去兼容所有的CPU,通過JMM我們才屏蔽了不同硬件和操作系統(tǒng)內存的訪問差異,這樣保證了Java程序在不同的平臺下達到一致的內存訪問效果,同時也是保證在高效并發(fā)的時候程序能夠正確執(zhí)行。
- 原子性:Java內存模型通過read、load、assign、use、store、write來保證原子性操作,此外還有l(wèi)ock和unlock,直接對應著synchronized關鍵字的monitorenter和monitorexit字節(jié)碼指令。
- 可見性:可見性的問題在上面的回答已經(jīng)說過,Java保證可見性可以認為通過volatile、synchronized、final來實現(xiàn)。
- 有序性:由于處理器和編譯器的重排序導致的有序性問題,Java通過volatile、synchronized來保證。
happen-before規(guī)則
雖然指令重排提高了并發(fā)的性能,但是Java虛擬機會對指令重排做出一些規(guī)則限制,并不能讓所有的指令都隨意的改變執(zhí)行位置,主要有以下幾點:
- 單線程每個操作,happen-before于該線程中任意后續(xù)操作
- volatile寫happen-before與后續(xù)對這個變量的讀
- synchronized解鎖happen-before后續(xù)對這個鎖的加鎖
- final變量的寫happen-before于final域對象的讀,happen-before后續(xù)對final變量的讀
- 傳遞性規(guī)則,A先于B,B先于C,那么A一定先于C發(fā)生
說了半天,到底工作內存和主內存是什么?
主內存可以認為就是物理內存,Java內存模型中實際就是虛擬機內存的一部分。而工作內存就是CPU緩存,他有可能是寄存器也有可能是L1\L2\L3緩存,都是有可能的。
多線程有什么用?
一個可能在很多人看來很扯淡的一個問題:我會用多線程就好了,還管它有什么用?在我看來,這個回答更扯淡。所謂"知其然知其所以然",“會用"只是"知其然”,“為什么用"才是"知其所以然”,只有達到"知其然知其所以然"的程度才可以說是把一個知識點運用自如。OK,下面說說我對這個問題的看法:
(1)發(fā)揮多核CPU的優(yōu)勢
隨著工業(yè)的進步,現(xiàn)在的筆記本、臺式機乃至商用的應用服務器至少也都是雙核的,4核、8核甚至16核的也都不少見,如果是單線程的程序,那么在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。單核CPU上所謂的"多線程"那是假的多線程,同一時間處理器只會處理一段邏輯,只不過線程之間切換得比較快,看著像多個線程"同時"運行罷了。多核CPU上的多線程才是真正的多線程,它能讓你的多段邏輯同時工作,多線程,可以真正發(fā)揮出多核CPU的優(yōu)勢來,達到充分利用CPU的目的。
(2)防止阻塞
從程序運行效率的角度來看,單核CPU不但不會發(fā)揮出多線程的優(yōu)勢,反而會因為在單核CPU上運行多線程導致線程上下文的切換,而降低程序整體的效率。但是單核CPU我們還是要應用多線程,就是為了防止阻塞。試想,如果單核CPU使用單線程,那么只要這個線程阻塞了,比方說遠程讀取某個數(shù)據(jù)吧,對端遲遲未返回又沒有設置超時時間,那么你的整個程序在數(shù)據(jù)返回回來之前就停止運行了。多線程可以防止這個問題,多條線程同時運行,哪怕一條線程的代碼執(zhí)行讀取數(shù)據(jù)阻塞,也不會影響其它任務的執(zhí)行。
(3)便于建模
這是另外一個沒有這么明顯的優(yōu)點了。假設有一個大的任務A,單線程編程,那么就要考慮很多,建立整個程序模型比較麻煩。但是如果把這個大的任務A分解成幾個小任務,任務B、任務C、任務D,分別建立程序模型,并通過多線程分別運行這幾個任務,那就簡單很多了。