更新時間:2019-12-13 10:17:26 來源:動力節點 瀏覽2213次
Volatile的特征:
A、禁止指令重排(有例外)
B、可見性
Volatile的內存語義:
當寫一個volatile變量時,JMM會把線程對應的本地內存中的共享變量值刷新到主內存。
當讀一個volatile變量時,JMM會把線程對應的本地內存置為無效,線程接下來將從主內存中讀取共享變量。
Volatile的重排序
1、當第二個操作為volatile寫操做時,不管第一個操作是什么(普通讀寫或者volatile讀寫),都不能進行重排序。這個規則確保volatile寫之前的所有操作都不會被重排序到volatile之后;
2、當第一個操作為volatile讀操作時,不管第二個操作是什么,都不能進行重排序。這個規則確保volatile讀之后的所有操作都不會被重排序到volatile之前;
3、當第一個操作是volatile寫操作時,第二個操作是volatile讀操作,不能進行重排序。
這個規則和前面兩個規則一起構成了:兩個volatile變量操作不能夠進行重排序;
除以上三種情況以外可以進行重排序。
比如:
1、第一個操作是普通變量讀/寫,第二個是volatile變量的讀;
2、第一個操作是volatile變量的寫,第二個是普通變量的讀/寫;
內存屏障/內存柵欄
內存屏障(Memory Barrier,或有時叫做內存柵欄,Memory Fence)是一種CPU指令,用于控制特定條件下的重排序和內存可見性問題。Java編譯器也會根據內存屏障的規則禁止重排序。(也就是讓一個CPU處理單元中的內存狀態對其它處理單元可見的一項技術。)
內存屏障可以被分為以下幾種類型:
LoadLoad屏障:對于這樣的語句Load1; LoadLoad; Load2,在Load2及后續讀取操作要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢。
StoreStore屏障:對于這樣的語句Store1; StoreStore; Store2,在Store2及后續寫入操作執行前,保證Store1的寫入操作對其它處理器可見。
LoadStore屏障:對于這樣的語句Load1; LoadStore; Store2,在Store2及后續寫入操作被刷出前,保證Load1要讀取的數據被讀取完畢。
StoreLoad屏障:對于這樣的語句Store1; StoreLoad; Load2,在Load2及后續所有讀取操作執行前,保證Store1的寫入對所有處理器可見。它的開銷是四種屏障中最大的。
在大多數處理器的實現中,這個屏障是個萬能屏障,兼具其它三種內存屏障的功能。
內存屏障阻礙了CPU采用優化技術來降低內存操作延遲,必須考慮因此帶來的性能損失。為了達到最佳性能,最好是把要解決的問題模塊化,這樣處理器可以按單元執行任務,然后在任務單元的邊界放上所有需要的內存屏障。采用這個方法可以讓處理器不受限的執行一個任務單元。合理的內存屏障組合還有一個好處是:緩沖區在第一次被刷后開銷會減少,因為再填充改緩沖區不需要額外工作了。
Java是如何實現跨平臺的?
跨平臺是怎樣實現的呢?這就要談及Java虛擬機(Java Virtual Machine,簡稱 JVM)。
JVM也是一個軟件,不同的平臺有不同的版本。我們編寫的Java源碼,編譯后會生成一種 .class 文件,稱為字節碼文件。Java虛擬機就是負責將字節碼文件翻譯成特定平臺下的機器碼然后運行。也就是說,只要在不同平臺上安裝對應的JVM,就可以運行字節碼文件,運行我們編寫的Java程序。
而這個過程中,我們編寫的Java程序沒有做任何改變,僅僅是通過JVM這一”中間層“,就能在不同平臺上運行,真正實現了”一次編譯,到處運行“的目的。
JVM是一個”橋梁“,是一個”中間件“,是實現跨平臺的關鍵,Java代碼首先被編譯成字節碼文件,再由JVM將字節碼文件翻譯成機器語言,從而達到運行Java程序的目的。
注意:編譯的結果不是生成機器碼,而是生成字節碼,字節碼不能直接運行,必須通過JVM翻譯成機器碼才能運行。不同平臺下編譯生成的字節碼是一樣的,但是由JVM翻譯成的機器碼卻不一樣。
所以,運行Java程序必須有JVM的支持,因為編譯的結果不是機器碼,必須要經過JVM的再次翻譯才能執行。即使你將Java程序打包成可執行文件(例如 .exe),仍然需要JVM的支持。
注意:跨平臺的是Java程序,不是JVM。JVM是用C/C++開發的,是編譯后的機器碼,不能跨平臺,不同平臺下需要安裝不同版本的JVM。
Java 線程有哪些狀態,這些狀態之間是如何轉化的?
新建(new):新創建了一個線程對象。
可運行(runnable):線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位于可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。
運行(running):可運行狀態(runnable)的線程獲得了cpu 時間片(timeslice) ,執行程序代碼。
阻塞(block):阻塞狀態是指線程因為某種原因放棄了cpu 使用權,也即讓出了cpu timeslice,暫時停止運行。直到線程進入可運行(runnable)狀態,才有機會再次獲得cpu timeslice 轉到運行(running)狀態。阻塞的情況分三種:
(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池(lock pool)中。
(三). 其他阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。
死亡(dead):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命周期。死亡的線程不可再次復生。
對于Java中多態的理解
所謂多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時并不確定,而是在程序運行期間才確定,即一個引用變量到底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。
多態的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而采用多種不同的行為方式。(發送消息就是函數調用)
Java實現多態有三個必要條件:繼承、重寫、父類引用指向子類對象。
繼承:在多態中必須存在有繼承關系的子類和父類。
重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。
父類引用指向子類對象:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備技能調用父類的方法和子類的方法。
實現多態的技術稱為:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。
多態的作用:消除類型之間的耦合關系。
以上就是動力節點Java培訓機構小編介紹的“阿里Java面試題經典部分”的內容,希望對大家有幫助,如有疑問,請在線咨詢,有專業老師隨時為你服務。
相關推薦
最新最全java面試題及答案(初級到高級)
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習