1. 程式人生 > 其它 >Java面試題中高階,阿里面試官:你好

Java面試題中高階,阿里面試官:你好

Java面試題中高階,阿里面試官:你好



`#increase1`方法表示的是修飾靜態方法;`#increase2`方法表示的是修飾例項方法;`#increase1`方法表示的是修飾程式碼塊。



[](https://gitee.com/vip204888/java-p7)三、鎖儲存佈局

--------------------------------------------------------------------------



`synchronized`始終與物件關聯。如果方法是靜態的,那麼關聯的物件就是類;如果該方法是非靜態的,則關聯的物件是例項。如果是程式碼塊,那麼就是指定的物件。很顯然,鎖是記錄於物件中。那麼問題來了,`synchronized`的鎖具體指的是什麼呢?簡單理解鎖就是一個共享的資源,記錄了誰佔有它,當前狀態是什麼等等。我們先來分析物件在記憶體中是如何儲存的。



在`Hotspot JVM` 中,`Java Object`物件在記憶體中儲存中的儲存佈局分為三個區域,分別是物件頭、示例資料、物件填充。如圖所示,陣列和物件的儲存佈局十分相似,只是物件的頭部大於陣列的長度,因為陣列需要儲存自身的長度,為4Byte。



![](https://img-blog.csdnimg.cn/img_convert/09db3d608aa4dfd4ad035dd734070f00.png)



從圖上可以看出,物件頭部包括兩部分,分別是物件標記和類元資訊(型別指標)。物件標記,也就是`Markword`儲存物件的 `hashCode`、 `GC` 資訊和鎖等資訊。類元資訊儲存“類物件資訊的指標”。在32位的 `JVM` 中,物件頭佔用8個byte,另外在64位的 `JVM` 佔用16個位元組。



![](https://img-blog.csdnimg.cn/img_convert/20a921120dc15d5f3041990d0cbea8b3.png)



如上圖所示,這個是`Markword`類在32位的 `JVM` 的各種情況儲存佈局,`Markword` 裡面儲存的資料會隨著鎖標誌位的變為而變化,大致儲存的變化共分為五種情況。我們可以從圖上看到從無鎖->偏向鎖->輕量級鎖->重量級鎖儲存的變化過程,這個就是鎖升級的過程。



那麼問題來了,是不是所有的物件都能實現鎖呢?答案是肯定的。



*   首先我們對於`Java`有一個共有的認知,那就是所以的物件都派生自`Object`,每個Object在記憶體中儲存都如我們圖上所示的,都有物件頭,物件頭中有`Markword`物件標記。 需要注意的是,物件儲存包括`Markword`物件標記的實現都是`native`的,都是`C++`語言實現的物件。

*   執行緒在獲取鎖時,實際獲取的是一個監視器(monitor)物件,這是一個同步物件,所有的`Java Object`都包含這個物件。同樣的,這個物件也是`native`的。



[](https://gitee.com/vip204888/java-p7)四、鎖升級

------------------------------------------------------------------------



`Java 1.6`之前,`synchronized`是標準的重量級鎖,多個執行緒競爭共享資源時,未競爭到資源的執行緒會一直處於阻塞狀態,效能開銷很大,同時對於重量級鎖,對於加鎖和釋放鎖也有很多的資源消耗。為了減少效能開銷,提升效率,人們針對不同的加鎖場景,細分了四種鎖狀態,包括無鎖、偏向鎖、輕量級鎖,重量級鎖,鎖的狀態會根據執行緒競爭資源的激烈程度從低到高不斷升級。



### [](https://gitee.com/vip204888/java-p7)4.1 偏向鎖



很多時候,鎖總是被同一執行緒多次獲取,並沒有執行緒競爭鎖。對於這樣的情況,偏向鎖就很適用,那到底什麼時候偏向鎖呢?在第三章節,我們列出了在`synchronized`不同的鎖狀態下,`Markword`記憶體佈局有很大的差異。



#### [](https://gitee.com/vip204888/java-p7)4.1.1 偏向鎖獲取



![](https://img-blog.csdnimg.cn/img_convert/74a77b83cc4b23216f0f5f0cfc92017f.png)



當一個執行緒去訪問`synchronized`關鍵字修飾的程式碼塊或方法時,會在`Markword`中儲存當前執行緒的ID,當再有執行緒想嘗試進入同步塊時,會先通過`CAS`比較當前`Markword`儲存的執行緒ID是否為嘗試進入同步塊的執行緒ID,如果相等,不需要再次獲取鎖了,可直接執行同步程式碼塊;如果不相等,說明當前偏向鎖是偏向於其它執行緒,需要撤銷偏向鎖,然後將鎖升級成輕量級鎖。



#### [](https://gitee.com/vip204888/java-p7)4.1.2 偏向鎖撤銷



撤銷偏向鎖並不是將鎖真正的撤銷,成為無鎖的狀態。對於偏向鎖的撤銷,對原持有的執行緒和鎖本身有兩種情況。



*   如果原持有執行緒剛好執行完了,退出同步程式碼塊,那麼這個時候會把`Markword`儲存的執行緒ID設定為空。

*   如果原持有執行緒仍在同步程式碼塊中執行,這個時候偏向鎖會升級為輕量級鎖,然後原有執行緒繼續執行。



下面圖演示在`synchronized`修飾的同步程式碼塊下,執行緒T1和執行緒T2先後競爭鎖資源的流程。



![](https://img-blog.csdnimg.cn/img_convert/18d154faeabe6d3861f399b1e4dade6c.png)



### [](https://gitee.com/vip204888/java-p7)4.2 輕量級鎖



上一小節說到了兩個執行緒競爭鎖,導致偏向鎖的撤銷,撤銷過程中有一種常見的鎖升級,即升級成輕量級鎖。輕量級鎖適用於兩個執行緒競爭鎖資源,並且同步程式碼塊執行很快的場景。那在物件中的`Markword`儲存佈局有變化成什麼呢?



#### [](https://gitee.com/vip204888/java-p7)4.2.1 輕量級升級過程



眾所周知,在`JVM`中,棧是執行緒私有的。升級成輕量級鎖的第一步是在棧的棧幀中搞事情。



1.  棧幀新建立鎖記錄`LockRecord`,記錄中包括`displaced hdr` 和`owner`。

2.  將鎖物件頭中的`Markword`內容複製到剛建立的棧幀中`LockRecord`。

3.  將鎖記錄`LockRecord`中的owner指向鎖物件。

4.  最後將物件頭的`Markword`中的指向棧中鎖記錄的指標指向鎖記錄`LockRecord`(這個步驟才是`Markword`儲存內容真正的變化)。



變化過程如下圖所示。



![](https://img-blog.csdnimg.cn/img_convert/ea0605bbbec31d47b59d70e57ac99342.png)



#### [](https://gitee.com/vip204888/java-p7)4.2.2 輕量級競爭過程



當一個執行緒佔有輕量級鎖時,當另一個執行緒來競爭時,這個執行緒會在原地空迴圈等待,而不是將執行緒狀態轉變為`Blocked`阻塞態。當佔有的執行緒離開同步塊,釋放鎖以後,另外一個執行緒就會迅速的獲取到鎖。



**那麼為什麼未獲取到鎖資源的執行緒是迴圈等待,而不是阻塞呢?** 這其中最重要的原因是執行緒的阻塞和喚醒需要`CPU`從使用者態轉為核心態,頻繁的阻塞和喚醒對CPU來說是一件負擔相當重的工作,勢必會給作業系統的併發效能帶來非常大的壓力。所以採取了迴圈去等待,這就是**自旋鎖**,這種方式在`AQS`鎖底層也用到了。



**那麼未獲取到鎖資源的執行緒是如何迴圈等待的呢?** 不停的迴圈會消耗`CPU`效能,這種自旋鎖當然是有停止條件的,分為兩種情況。



*   在`Java 1.6` 之前,設定了一個自旋的次數,超過迴圈次數就會迴圈就會終止,一般設定的次數是10,可以通過設定`HotSpot 引數 -XX:PreBlockSpin`來修改,修改這個引數之前需通先通過設定引數`-XX:+UseSpining`開啟自旋鎖。

*   在`Java 1.6` 之後,引入了相較於智慧的自適應自旋鎖,這種方式是根據前一次在同樣的鎖自選的時間和鎖的狀態決定鎖的自選時間,而不是固定自旋次數。



#### [](https://gitee.com/vip204888/java-p7)4.2.3 自旋鎖鎖釋放




# Kafka實戰筆記

> **關於這份筆記,為了不影響大家的閱讀體驗,我只能在文章中展示部分的章節內容和核心截圖,如果你需要完整的pdf版本,[戳這裡即可免費領取](https://gitee.com/vip204888/java-p7)。**

![image.png](https://upload-images.jianshu.io/upload_images/24616006-873dd5cc89b3be85.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


*   **Kafka入門**
*   **為什麼選擇Kafka**
*   **Karka的安裝、管理和配置**

![image.png](https://upload-images.jianshu.io/upload_images/24616006-fda63abfaa0ad0a7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


*   **Kafka的叢集**
*   **第一個Kafka程式**
*   ![image.png](https://upload-images.jianshu.io/upload_images/24616006-2e6de422473d83e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


afka的生產者

![image.png](https://upload-images.jianshu.io/upload_images/24616006-b3e867a2753f813c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

*   **Kafka的消費者**
*   **深入理解Kafka**
*   **可靠的資料傳遞**

![image.png](https://upload-images.jianshu.io/upload_images/24616006-f70d334064187a5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


![image.png](https://upload-images.jianshu.io/upload_images/24616006-a47e04661cf25927.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


*   **Spring和Kalka的整合**
*   **Sprinboot和Kafka的整合**
*   **Kafka實戰之削峰填谷**
*   **資料管道和流式處理(瞭解即可)**

![image.png](https://upload-images.jianshu.io/upload_images/24616006-c5e75f2bce823d47.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


*   **Kafka實戰之削峰填谷**

![image.png](https://upload-images.jianshu.io/upload_images/24616006-4699f8dcfb39f152.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)