Java中爲什麼沒有指針
- 棧中的變量指向堆內存的變量
- Java中所有的基本數據類型的傳遞都是值傳遞,除此之外,其他任何傳遞都是地址傳遞
static關鍵字
- 作用:方便在沒有創建對象的情況下調用
- 特點:不能修飾局部變量,也可以通過對象來調用
- 與非靜態變量的區別 :
-
- 靜態變量被所有對象共享,僅一個副本,在類被加載時初始化,且初始化的順序按照被定義的順序
-
- 非靜態變量是對象所擁有的,存在多個副本,在創建對象的時候被初始化
string,stringgbuffer,stringbuilder區別
\ | string | stringbuffer | stringbuilder |
---|---|---|---|
是否可變 | 不可 | 可 | 可 |
效率 | 低 | 低 | 高 |
線程安全 | 安全 | 安全 | 不安全 |
是否可空值 | 可 | 否 | 否 |
final,finally,finalize區別
- final:聲明屬性、方法和類時,表示屬性不可變、方法不可被覆蓋、類不可被繼承
- finally:異常處理語句結構中總是被執行的一部分
- finalize:在垃圾回收機制中,回收此前未回收的內存垃圾,所有object都繼承了
== 和equals區別
- ==的作用:基本類型時比較的是值是否相同,引用類型時比較地址是否相同
- equals的作用:在新類中覆蓋了equals方法時,比較的是地址中的值;否則比較的是地址
HashTable 和 HashMap區別
\ | 線程安全 | 允許有null的健和值 | 效率 | 方法是否synchronize |
---|---|---|---|---|
hashmap | 不安全 | 是 | 高 | 不是 |
hashtable | 安全 | 不是 | 低 | 是 |
-
- hashset底層基於hashmap實現,元素不重複
-
- 多線程下,hashmap進行put操作會引起死循環,CPU利用率100%
-
- ConcurrentHashMap,JDK8之後,採用了 CAS + synchronized 來保證併發安全性。
-
- Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但二者都實現了Map接口。
\ | 線程安全 | 初始空間 | 增長空間 |
---|---|---|---|
vector | 安全 | 可設置 | 2倍且可設置 |
ArrayList | 不安全 | 可設置 | 1.5且不可設置 |
volatile的作用
- 多個線程對該變量的內存可見性
- 禁止指令重排序
volatile和synchronized的區別
\ | 變量可見性 | 原子性 | 阻塞 | 指令重排序 | 僅當前線程訪問 |
---|---|---|---|---|---|
volatile | 是 | 否 | 否 | 禁止 | 否 |
synchronized | 是 | 是 | 是 | 不禁止 | 是 |
synchronized與lock的區別
\ | 存在層次 | 鎖釋放 | 鎖狀態 | 鎖類型 | 性能 |
---|---|---|---|---|---|
synchronized | jvm層面 | 線程執行完成或異常則釋放 | 無法判斷 | 可重入 | 少量同步 |
lock | 一個類 | finally中必須釋放 | 可判斷 | 可重入 | 大量同步 |
- 可重入鎖:某線程已經獲得鎖,可以再次獲得該鎖而不會發生死鎖。synchronized和ReentrantLock都是可重入的,其中使用 ReentrantLock的時候一定要手動釋放鎖
- 兩者均是獨佔鎖,是一種給悲觀鎖
sleep() 與 wait() 與 yield() 的區別
- sleep() 線程進入阻塞狀態,wait() 線程被掛起
- 前者會讓出CPU,但是不會釋放同步資源;wait兩個都會讓出
- yield不釋放鎖,進入可執行狀態
併發的問題
- 爲什麼併發不一定快?(上下文切換)
- 線程有哪些分類?(守護線程、用戶線程
- 使用多線程的方法?(Thread、Runnable、ExecutorService
- 緩存一致性問題?
-
- 當程序在運行過程中,會將運算需要的數據從主存複製一份到cup的高速緩存當中,那麼cpu進行計算時就可以直接從它的高速緩存讀取數據和向其中寫入數據,當運算結束後,再將高速緩存中的數據刷新到主存當中
- 鎖的幾種狀態:無鎖->偏向鎖→輕量鎖→重量鎖
- CAS是什麼?
-
- 比較並交換。包含三個參數,內存位置、預期原值、新值。如果內存位置的原值與預期相同,則替換成新值;否則,就不替換
- 重排序的分類:編譯器重排序、指令級重排序、內存級重排序
- 爲什麼我們調用 start() 方法時會執行 run() 方法,爲什麼我們不能直接調用 run() 方法?
-
- new 一個 Thread,線程進入了新建狀態;調用 start() 方法,會啓動一個線程並使線程進入了就緒狀態,當分配到時間片後就可以開始運行了。 start() 會執行線程的相應準備工作,然後自動執行 run() 方法的內容,這是真正的多線程工作。 而直接執行 run() 方法,會把 run 方法當成一個 main 線程下的普通方法去執行,並不會在某個線程中執行它,所以這並不是多線程工作。
\ | 優點 | 缺點 | 適用場景 |
---|---|---|---|
偏向鎖 | 加鎖解鎖無需額外消耗 | 競爭線程多時會帶來額外消耗 | 基本沒有線程競爭 |
輕量級鎖 | 線程競爭不會阻塞,使用自旋 | 如果一直不能獲取鎖,長時間的自旋造成CPU消耗 | 少量進程競爭鎖 |
重量級鎖 | 進程會阻塞,CPU不會空轉消耗CPU | 線程阻塞 | 大量進程競爭CPU |
鎖總結
- 常見面試.