Volatile的特征:
A、禁止指令重排(有例外)
B、可見性
Volatile的內(nèi)存語義:
當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存。
當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把線程對(duì)應(yīng)的本地內(nèi)存置為無效,線程接下來將從主內(nèi)存中讀取共享變量。
Volatile的重排序
1、當(dāng)?shù)诙€(gè)操作為volatile寫操做時(shí),不管第一個(gè)操作是什么(普通讀寫或者volatile讀寫),都不能進(jìn)行重排序。這個(gè)規(guī)則確保volatile寫之前的所有操作都不會(huì)被重排序到volatile之后;
2、當(dāng)?shù)谝粋€(gè)操作為volatile讀操作時(shí),不管第二個(gè)操作是什么,都不能進(jìn)行重排序。這個(gè)規(guī)則確保volatile讀之后的所有操作都不會(huì)被重排序到volatile之前;
3、當(dāng)?shù)谝粋€(gè)操作是volatile寫操作時(shí),第二個(gè)操作是volatile讀操作,不能進(jìn)行重排序。
這個(gè)規(guī)則和前面兩個(gè)規(guī)則一起構(gòu)成了:兩個(gè)volatile變量操作不能夠進(jìn)行重排序;
除以上三種情況以外可以進(jìn)行重排序。
比如:
1、第一個(gè)操作是普通變量讀/寫,第二個(gè)是volatile變量的讀;
2、第一個(gè)操作是volatile變量的寫,第二個(gè)是普通變量的讀/寫;
內(nèi)存屏障/內(nèi)存柵欄
內(nèi)存屏障(Memory Barrier,或有時(shí)叫做內(nèi)存柵欄,Memory Fence)是一種CPU指令,用于控制特定條件下的重排序和內(nèi)存可見性問題。Java編譯器也會(huì)根據(jù)內(nèi)存屏障的規(guī)則禁止重排序。(也就是讓一個(gè)CPU處理單元中的內(nèi)存狀態(tài)對(duì)其它處理單元可見的一項(xiàng)技術(shù)。)
內(nèi)存屏障可以被分為以下幾種類型:
LoadLoad屏障:對(duì)于這樣的語句Load1; LoadLoad; Load2,在Load2及后續(xù)讀取操作要讀取的數(shù)據(jù)被訪問前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
StoreStore屏障:對(duì)于這樣的語句Store1; StoreStore; Store2,在Store2及后續(xù)寫入操作執(zhí)行前,保證Store1的寫入操作對(duì)其它處理器可見。
LoadStore屏障:對(duì)于這樣的語句Load1; LoadStore; Store2,在Store2及后續(xù)寫入操作被刷出前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
StoreLoad屏障:對(duì)于這樣的語句Store1; StoreLoad; Load2,在Load2及后續(xù)所有讀取操作執(zhí)行前,保證Store1的寫入對(duì)所有處理器可見。它的開銷是四種屏障中最大的。
在大多數(shù)處理器的實(shí)現(xiàn)中,這個(gè)屏障是個(gè)萬能屏障,兼具其它三種內(nèi)存屏障的功能。
內(nèi)存屏障阻礙了CPU采用優(yōu)化技術(shù)來降低內(nèi)存操作延遲,必須考慮因此帶來的性能損失。為了達(dá)到最佳性能,最好是把要解決的問題模塊化,這樣處理器可以按單元執(zhí)行任務(wù),然后在任務(wù)單元的邊界放上所有需要的內(nèi)存屏障。采用這個(gè)方法可以讓處理器不受限的執(zhí)行一個(gè)任務(wù)單元。合理的內(nèi)存屏障組合還有一個(gè)好處是:緩沖區(qū)在第一次被刷后開銷會(huì)減少,因?yàn)樵偬畛涓木彌_區(qū)不需要額外工作了。
Java是如何實(shí)現(xiàn)跨平臺(tái)的?
跨平臺(tái)是怎樣實(shí)現(xiàn)的呢?這就要談及Java虛擬機(jī)(Java Virtual Machine,簡稱 JVM)。
JVM也是一個(gè)軟件,不同的平臺(tái)有不同的版本。我們編寫的Java源碼,編譯后會(huì)生成一種 .class 文件,稱為字節(jié)碼文件。Java虛擬機(jī)就是負(fù)責(zé)將字節(jié)碼文件翻譯成特定平臺(tái)下的機(jī)器碼然后運(yùn)行。也就是說,只要在不同平臺(tái)上安裝對(duì)應(yīng)的JVM,就可以運(yùn)行字節(jié)碼文件,運(yùn)行我們編寫的Java程序。
而這個(gè)過程中,我們編寫的Java程序沒有做任何改變,僅僅是通過JVM這一”中間層“,就能在不同平臺(tái)上運(yùn)行,真正實(shí)現(xiàn)了”一次編譯,到處運(yùn)行“的目的。
JVM是一個(gè)”橋梁“,是一個(gè)”中間件“,是實(shí)現(xiàn)跨平臺(tái)的關(guān)鍵,Java代碼首先被編譯成字節(jié)碼文件,再由JVM將字節(jié)碼文件翻譯成機(jī)器語言,從而達(dá)到運(yùn)行Java程序的目的。
注意:編譯的結(jié)果不是生成機(jī)器碼,而是生成字節(jié)碼,字節(jié)碼不能直接運(yùn)行,必須通過JVM翻譯成機(jī)器碼才能運(yùn)行。不同平臺(tái)下編譯生成的字節(jié)碼是一樣的,但是由JVM翻譯成的機(jī)器碼卻不一樣。
所以,運(yùn)行Java程序必須有JVM的支持,因?yàn)榫幾g的結(jié)果不是機(jī)器碼,必須要經(jīng)過JVM的再次翻譯才能執(zhí)行。即使你將Java程序打包成可執(zhí)行文件(例如 .exe),仍然需要JVM的支持。
注意:跨平臺(tái)的是Java程序,不是JVM。JVM是用C/C++開發(fā)的,是編譯后的機(jī)器碼,不能跨平臺(tái),不同平臺(tái)下需要安裝不同版本的JVM。
Java 線程有哪些狀態(tài),這些狀態(tài)之間是如何轉(zhuǎn)化的?
新建(new):新創(chuàng)建了一個(gè)線程對(duì)象。
可運(yùn)行(runnable):線程對(duì)象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,等待被線程調(diào)度選中,獲取cpu 的使用權(quán) 。
運(yùn)行(running):可運(yùn)行狀態(tài)(runnable)的線程獲得了cpu 時(shí)間片(timeslice) ,執(zhí)行程序代碼。
阻塞(block):阻塞狀態(tài)是指線程因?yàn)槟撤N原因放棄了cpu 使用權(quán),也即讓出了cpu timeslice,暫時(shí)停止運(yùn)行。直到線程進(jìn)入可運(yùn)行(runnable)狀態(tài),才有機(jī)會(huì)再次獲得cpu timeslice 轉(zhuǎn)到運(yùn)行(running)狀態(tài)。阻塞的情況分三種:
(一). 等待阻塞:運(yùn)行(running)的線程執(zhí)行o.wait()方法,JVM會(huì)把該線程放入等待隊(duì)列(waitting queue)中。
(二). 同步阻塞:運(yùn)行(running)的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM會(huì)把該線程放入鎖池(lock pool)中。
(三). 其他阻塞:運(yùn)行(running)的線程執(zhí)行Thread.sleep(long ms)或t.join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入可運(yùn)行(runnable)狀態(tài)。
死亡(dead):線程run()、main() 方法執(zhí)行結(jié)束,或者因異常退出了run()方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。
對(duì)于Java中多態(tài)的理解
所謂多態(tài)就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會(huì)指向哪個(gè)類的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定。因?yàn)樵诔绦蜻\(yùn)行時(shí)才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實(shí)現(xiàn)上,從而導(dǎo)致該引用調(diào)用的具體方法隨之改變,即不修改程序代碼就可以改變程序運(yùn)行時(shí)所綁定的具體代碼,讓程序可以選擇多個(gè)運(yùn)行狀態(tài),這就是多態(tài)性。
多態(tài)的定義:指允許不同類的對(duì)象對(duì)同一消息做出響應(yīng)。即同一消息可以根據(jù)發(fā)送對(duì)象的不同而采用多種不同的行為方式。(發(fā)送消息就是函數(shù)調(diào)用)
Java實(shí)現(xiàn)多態(tài)有三個(gè)必要條件:繼承、重寫、父類引用指向子類對(duì)象。
繼承:在多態(tài)中必須存在有繼承關(guān)系的子類和父類。
重寫:子類對(duì)父類中某些方法進(jìn)行重新定義,在調(diào)用這些方法時(shí)就會(huì)調(diào)用子類的方法。
父類引用指向子類對(duì)象:在多態(tài)中需要將子類的引用賦給父類對(duì)象,只有這樣該引用才能夠具備技能調(diào)用父類的方法和子類的方法。
實(shí)現(xiàn)多態(tài)的技術(shù)稱為:動(dòng)態(tài)綁定(dynamic binding),是指在執(zhí)行期間判斷所引用對(duì)象的實(shí)際類型,根據(jù)其實(shí)際的類型調(diào)用其相應(yīng)的方法。
多態(tài)的作用:消除類型之間的耦合關(guān)系。
以上就是北大青鳥長沙麓谷校區(qū)Java培訓(xùn)機(jī)構(gòu)小編介紹的“阿里Java面試題經(jīng)典部分”的內(nèi)容,希望對(duì)大家有幫助,如有疑問,請(qǐng)?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為你服務(wù)。
相關(guān)推薦
最新最全java面試題及答案(初級(jí)到高級(jí))
史上最全的中高級(jí)JAVA工程師面試題及答案匯總
Java高級(jí)開發(fā)工程師面試題
2019史上最全java面試題題庫大全800題
哪有資深java工程師面試題
Java面試題