Android第四次面試(Java基礎篇)

一、Java 中的 DCL 單例模式

? 單例模式是設計模式中最常用的模式之一,其核心目標是確保一個類在程序中僅有一個實例,并提供全局訪問點。在 Java 中,實現單例模式需要兼顧線程安全和性能優化。DCL(Double-Checked Locking,雙重檢查鎖定)單例模式通過巧妙的鎖機制,在保證線程安全的同時顯著提升了性能,成為高并發場景下的首選方案。

DCL 單例模式實現代碼

public class DCLSingleton {// 使用volatile保證可見性和禁止指令重排private static volatile DCLSingleton instance;// 私有構造函數,禁止外部實例化private DCLSingleton() {}// 雙重檢查鎖定的核心方法public static DCLSingleton getInstance() {// 第一次檢查:避免無意義的同步if (instance == null) {synchronized (DCLSingleton.class) { // 同步類鎖// 第二次檢查:確保唯一實例化if (instance == null) {instance = new DCLSingleton();}}}return instance;}
}

代碼解釋

  1. volatile?關鍵字instance?變量使用?volatile?修飾,它有兩個重要作用。一是保證變量的可見性,即一個線程修改了該變量的值,其他線程能立即看到最新的值。二是禁止指令重排,避免在多線程環境下,new DCLSingleton()?操作可能出現的問題,如對象還未完全初始化就被其他線程使用。
  2. 私有構造函數private DCLSingleton()?阻止了外部代碼通過?new?關鍵字創建該類的實例。
  3. 雙重檢查鎖
    • 第一次檢查?if (instance == null):在沒有進入同步塊之前先檢查?instance?是否為?null,如果不為?null?則直接返回實例,避免了每次調用?getInstance()?方法都進行同步操作,提高了性能。
    • 同步塊?synchronized (DCLSingleton.class):當多個線程同時通過第一次檢查時,會進入同步塊。
    • 第二次檢查?if (instance == null):在同步塊內再次檢查?instance?是否為?null,因為可能在第一個線程進入同步塊創建實例的過程中,其他線程也通過了第一次檢查并等待進入同步塊。如果沒有第二次檢查,可能會創建多個實例。

具體擴展:

1. 第一次檢查?if (instance == null)

  • 目的:提高性能。在單例模式中,getInstance()?方法可能會被頻繁調用。如果每次調用都進行同步操作(即進入?synchronized?塊),會帶來較大的性能開銷,因為同步操作涉及到線程的阻塞和喚醒,是比較耗時的。通過在進入同步塊之前先檢查?instance?是否為?null,如果不為?null,說明單例實例已經被創建,直接返回該實例,無需進行同步操作,這樣就避免了不必要的同步開銷,提高了程序的性能。
  • 多線程情況分析:在多線程環境下,多個線程可能同時調用?getInstance()?方法。由于此時還未進入同步塊,多個線程可能都會通過這第一次檢查,認為?instance?為?null,從而繼續執行后續代碼進入同步塊。

2. 同步塊?synchronized (DCLSingleton.class)

  • 目的:保證線程安全。當多個線程同時通過第一次檢查后,為了避免多個線程同時創建單例實例,需要使用同步機制。這里使用?synchronized?關鍵字對?DCLSingleton?類進行加鎖,確保同一時刻只有一個線程能夠進入同步塊執行后續代碼。
  • 鎖的范圍:使用類級別的鎖?DCLSingleton.class,這意味著所有線程在訪問這個同步塊時都需要競爭同一把鎖。這樣可以保證在同一時刻只有一個線程能夠執行同步塊內的代碼,從而避免多個線程同時創建多個單例實例。

3. 第二次檢查?if (instance == null)

  • 目的:防止創建多個實例。雖然通過第一次檢查和同步塊的機制,理論上可以保證單例實例的唯一性,但在多線程環境下仍存在一個問題。假設線程 A 和線程 B 同時通過了第一次檢查,線程 A 先進入同步塊創建了單例實例,然后線程 B 獲得鎖進入同步塊。如果沒有第二次檢查,線程 B 會再次創建一個新的實例,這就破壞了單例模式的原則。因此,在同步塊內再次檢查?instance?是否為?null,如果為?null?才創建實例,這樣就確保了單例實例的唯一性。
  • 多線程情況分析:當線程 A 進入同步塊創建實例后,instance?不再為?null。此時線程 B 進入同步塊,通過第二次檢查發現?instance?不為?null,就不會再創建新的實例,而是直接跳過創建實例的代碼。

二、Java HashMap 深度解析

? HashMap 是 Java 開發者最常用的數據結構之一,其底層基于哈希表實現,結合數組、鏈表和紅黑樹的特性,在大多數場景下都能提供高效的增刪查操作。本文將深入剖析 HashMap 的工作原理、核心實現細節及優化策略,幫助讀者理解其設計哲學與工程實踐。

工作原理

HashMap?的核心工作原理可以概括為:通過哈希函數將鍵(key)映射到數組的某個位置,當發生哈希沖突時,使用鏈表或紅黑樹來處理。其工作流程主要包含以下幾個步驟:

  1. 初始化:在創建?HashMap?對象時,會初始化一個數組(也稱為哈希桶),默認初始容量為 16,負載因子為 0.75。負載因子表示當數組中元素數量達到容量的一定比例時,會進行擴容操作。

  2. 插入元素:當調用?put(key, value)?方法插入鍵值對時,首先會計算鍵的哈希值,然后根據哈希值找到對應的數組位置。如果該位置為空,直接將鍵值對存儲在該位置;如果該位置已經有元素,說明發生了哈希沖突,此時會根據具體情況處理。在 JDK 8 及以后的版本中,如果鏈表長度達到 8 且數組長度達到 64,會將鏈表轉換為紅黑樹,以提高查找效率;如果紅黑樹節點數量小于 6,會將紅黑樹轉換回鏈表。

  3. 查找元素:當調用?get(key)?方法查找元素時,同樣會先計算鍵的哈希值,然后根據哈希值找到對應的數組位置。如果該位置只有一個元素,直接比較鍵是否相等;如果該位置是鏈表或紅黑樹,會在鏈表或紅黑樹中查找鍵對應的元素。

  4. 擴容:當數組中元素數量達到容量乘以負載因子時,會觸發擴容操作。擴容時會創建一個新的數組,容量為原來的 2 倍,然后將原數組中的元素重新哈希到新數組中。

具體計算過程

1. 計算哈希值

HashMap?中使用?hash()?方法計算鍵的哈希值,該方法會對鍵的?hashCode()?方法返回的值進行二次哈希,以減少哈希沖突的概率。具體代碼如下:

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

這里?key.hashCode()?是鍵對象的哈希碼,h >>> 16?是將哈希碼右移 16 位,然后進行異或運算。這樣做的目的是將哈希碼的高位和低位進行混合,使得哈希值更加均勻地分布在數組中。

2. 計算數組索引

計算出哈希值后,需要根據哈希值計算數組的索引位置。HashMap?中使用以下公式計算數組索引:

index = (n - 1) & hash

?其中?n?是數組的長度,hash?是鍵的哈希值。由于數組長度通常是 2 的冪次方,n - 1?的二進制表示中低位都是 1,通過與哈希值進行按位與運算,可以確保計算出的索引值在數組的有效范圍內。

3. 處理數組位置的元素
  • 位置為空:若該位置為空,就直接把鍵值對存儲在這個位置。
  • 位置已有元素(發生哈希沖突):這時候要依據具體情形處理。
    • JDK 8 之前:采用鏈表來處理哈希沖突。新插入的元素會被添加到鏈表的頭部,查找時需要遍歷鏈表,時間復雜度為?O(n),其中?n?是鏈表的長度。
    • JDK 8 及以后:引入了紅黑樹來優化鏈表過長時的查找性能。
      • 鏈表轉換為紅黑樹:當鏈表長度達到 8 并且數組長度達到 64 時,鏈表會被轉換為紅黑樹。紅黑樹是一種自平衡的二叉搜索樹,其查找、插入和刪除操作的時間復雜度為?O(logn),在處理大量數據時性能優于鏈表。
      • 紅黑樹轉換為鏈表:當紅黑樹的節點數量小于 6 時,紅黑樹會被轉換回鏈表。這是為了避免在數據量較小時,紅黑樹的維護開銷過大。

以下是簡化版的?put?方法代碼,用于說明插入元素的邏輯:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)// 位置為空,直接插入tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)// 是紅黑樹節點,插入到紅黑樹中e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {// 是鏈表節點,遍歷鏈表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st// 鏈表長度達到 8,轉換為紅黑樹treeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}
  • 性能優化:在數據量較小的時候,鏈表的插入和刪除操作比較簡單,開銷較小;而當數據量增大,鏈表長度變長時,查找效率會顯著下降,此時轉換為紅黑樹可以將查找時間復雜度從?O(n)?降低到?O(logn),提高整體性能。
  • 空間與時間的平衡:紅黑樹的維護需要額外的空間和操作,在數據量較小時,使用鏈表可以減少空間開銷;當數據量達到一定程度時,使用紅黑樹可以在時間復雜度上獲得更好的表現。

總結:

? HashMap 基于哈希表,底層由數組、鏈表(JDK7)和紅黑樹(JDK8+)組成,通過哈希值快速定位存儲位置。通過key.hashCode()與高位異或運算生成哈希值,結合數組長度(2 的冪)的位運算確定索引。沖突元素存入鏈表,當鏈表長度≥8 且數組長度≥64 時轉為紅黑樹,提升查找效率至 O (log n)。容量不足時(元素數超過閾值),數組擴容為原來的 2 倍,重新分配哈希桶并遷移元素。?合理設置初始容量和負載因子(默認 0.75),避免哈希沖突,多線程場景使用 ConcurrentHashMap。

擴展內容:

ConcurrentHashMap 的優勢

1. 線程安全
  • 傳統?HashMap?問題HashMap?是非線程安全的,在多線程環境下進行讀寫操作可能會導致數據不一致、死循環等問題,例如在擴容時可能會形成環形鏈表。
  • ConcurrentHashMap?解決方案ConcurrentHashMap?是線程安全的,它允許多個線程同時進行讀寫操作,不會出現數據不一致的問題。這使得開發者在多線程環境中無需額外的同步機制,降低了編程復雜度。
2. 高性能
  • 細粒度鎖:在 JDK 7 及以前,ConcurrentHashMap?使用分段鎖機制,將整個哈希表分成多個段(Segment),每個段相當于一個小的?HashMap,不同的段可以被不同的線程同時訪問,從而提高了并發性能。在 JDK 8 及以后,采用了 CAS(Compare-And-Swap)和 synchronized 相結合的方式,鎖的粒度進一步細化到節點級別,減少了鎖的競爭,提高了并發度。
  • 并發操作:多個線程可以同時對不同的桶進行讀寫操作,大大提高了并發性能。例如,在高并發場景下,多個線程可以同時對不同的鍵值對進行插入、刪除和查找操作,而不會相互阻塞。
3. 內存效率高
  • 減少鎖的開銷:由于采用了細粒度的鎖機制,減少了鎖的持有時間和范圍,降低了鎖的開銷,從而提高了內存的使用效率。

ConcurrentHashMap 的工作原理

JDK 7 及以前的分段鎖機制
  • 數據結構ConcurrentHashMap?內部包含一個?Segment?數組,每個?Segment?繼承自?ReentrantLock,相當于一個小的?HashMap。每個?Segment?包含一個?HashEntry?數組,用于存儲鍵值對。
  • 并發控制:當進行讀寫操作時,首先根據鍵的哈希值找到對應的?Segment,然后對該?Segment?加鎖。不同的?Segment?可以被不同的線程同時訪問,從而實現了并發操作。例如,線程 A 對?Segment1?進行寫操作,線程 B 可以同時對?Segment2?進行寫操作,只要它們操作的是不同的?Segment
JDK 8 及以后的 CAS 和 synchronized 機制
  • 數據結構:采用數組 + 鏈表 + 紅黑樹的結構,與?HashMap?類似。
  • 并發控制
    • CAS(Compare-And-Swap):在插入或更新節點時,首先使用 CAS 操作嘗試更新節點的值。CAS 是一種無鎖算法,它通過比較內存中的值和預期值是否相等來決定是否更新,避免了鎖的使用,提高了并發性能。例如,在插入一個新的節點時,首先使用 CAS 操作將新節點插入到指定的位置,如果 CAS 操作失敗,說明有其他線程已經修改了該位置的值,此時再使用 synchronized 進行加鎖操作。
    • synchronized:當 CAS 操作失敗或需要對鏈表或紅黑樹進行操作時,使用 synchronized 對節點進行加鎖。在 JDK 8 中,synchronized 進行了優化,鎖的粒度細化到節點級別,減少了鎖的競爭。例如,當多個線程同時對同一個鏈表進行操作時,只會對鏈表的頭節點進行加鎖,其他線程可以繼續對其他鏈表進行操作。

示例代碼

?

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 插入元素map.put("apple", 1);map.put("banana", 2);// 獲取元素Integer value = map.get("apple");System.out.println("Value of apple: " + value);// 并發操作示例Thread t1 = new Thread(() -> {map.put("cherry", 3);});Thread t2 = new Thread(() -> {Integer val = map.get("banana");System.out.println("Value of banana in thread 2: " + val);});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final map: " + map);}
}

?總結:ConcurrentHashMap?通過分段鎖(JDK7)或 CAS+synchronized 細粒度鎖(JDK8)實現線程安全,允許多線程并發讀寫,性能遠超線程不安全的?HashMap。底層采用數組 + 鏈表 / 紅黑樹結構(類似?HashMap),通過哈希值定位節點,JDK8 后利用 CAS 無鎖算法和節點級鎖減少競爭,提升并發效率。適用于高并發場景(如分布式系統、緩存),提供線程安全的高效操作,避免?HashMap?的多線程問題,同時比?Hashtable?的全表鎖更輕量。

?感謝觀看!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/74115.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/74115.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/74115.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java-SpringBootWeb入門、Spring官方腳手架連接不上解決方法

一. Spring 官網&#xff1a;Spring | Home Spring發展到今天已經形成了一種開發生態圈&#xff0c;Spring提供了若干個子項目&#xff0c;每個項目用于完成特定的功能(Spring全家桶) Spring Boot可以幫助我們非常快速的構建應用程序、簡化開發、提高效率 。 二. Spring Boot入…

1.7 無窮小的比較

1.定義 2.性質 3.無窮小的比較 3.1等價無窮小的性質 3.2 常見等價無窮小

StarRocks 升級注意事項

前段時間升級了生產環境的 StarRocks&#xff0c;從 3.3.3 升級到了 3.3.9&#xff0c;期間還是踩了不少坑所以在這里記錄下。 因為我們的集群使用的是存算分離的版本&#xff0c;也是使用官方提供的 operator 部署在 kubernetes 里的&#xff0c;所以沒法按照官方的流程進入虛…

深入探究 JVM 堆的垃圾回收機制(一)— 判活

垃圾回收分為兩步&#xff1a;1&#xff09;判定對象是否存活。2&#xff09;將“消亡”的對象進行內存回收。 1 判定對象存活 可達性分析算法&#xff1a;通過一系列“GC Roots”對象作為起始節點集&#xff0c;從這些節點開始&#xff0c;根據引用關系向下搜索&#xff0c;…

國產開發板—米爾全志T113-i如何實現ARM+RISC-V+DSP協同計算?

近年來&#xff0c;隨著半導體產業的快速發展和技術的不斷迭代&#xff0c;物聯網設備種類繁多&#xff08;如智能家居、工業傳感器&#xff09;&#xff0c;對算力、功耗、實時性要求差異大&#xff0c;單一架構無法滿足所有需求。因此米爾推出MYD-YT113i開發板&#xff08;基…

Tomcat虛擬主機配置詳解:Centos環境下多域名部署(詳細教程!)

&#x1f3e1;作者主頁&#xff1a;點擊&#xff01; Tomcat服務器&#x1f4dd;專欄&#xff1a;點擊&#xff01; &#x1f427;Linux高級管理防護和群集專欄&#xff1a;點擊&#xff01; ??創作時間&#xff1a;2025年3月18日14點14分 最近在折騰 Tomcat 的時候&…

鴻蒙開發工程師簡歷項目撰寫全攻略

一、項目結構的黃金法則 建議采用「41」結構&#xff1a; 項目背景&#xff08;業務價值&#xff09;技術架構&#xff08;鴻蒙特性&#xff09;核心實現&#xff08;技術難點&#xff09;個人貢獻&#xff08;量化成果&#xff09;附加價值&#xff08;延伸影響&#xff09; …

dfs刷題排列問題 + 子集問題 + 組和問題總結

文章目錄 一、排列問題全排列II題解代碼 優美的排列題解代碼 二、子集問題字母大小寫全排列題解代碼 找出所有子集的異或總和再求和題解代碼 三、組合問題電話號碼的字母組合題解代碼 括號生成題解代碼 組合題解代碼 目標和題解代碼 組合總和題解代碼 總結 一、排列問題 全排列…

【Linux】VMware17 安裝 Ubuntu24.04 虛擬機

目錄 安裝教程 一、下載 Ubuntu 桌面版iso映像 二、安裝 VMware 三、安裝 Ubuntu 桌面版 VMware 創建虛擬機 掛載 Ubuntu ISO 安裝 Ubuntu 系統 安裝教程 一、下載 Ubuntu 桌面版iso映像 鏈接來自 清華大學開源軟件鏡像站 ISO文件地址&#xff1a;ubuntu-24.04.2-des…

CVPR2025 | 對抗樣本智能安全方向論文匯總 | 持續更新中~

匯總結果來源&#xff1a;CVPR 2025 Accepted Papers 若文中出現的 論文鏈接 和 GitHub鏈接 點不開&#xff0c;則說明還未公布&#xff0c;在公布后筆者會及時添加. 若筆者未及時添加&#xff0c;歡迎讀者告知. 文章根據題目關鍵詞搜索&#xff0c;可能會有遺漏. 若筆者出現…

PostgreSQL_數據回退,數據庫導出、導入

目錄 前置&#xff1a; 1 數據回退 1.1 代碼 1.2 pgAdmin4 中查看 1&#xff09;t_daily 2) t_stock_daily 2 數據庫導出、導入 前置&#xff1a; 本博文是一個系列。在本人“數據庫專欄”-》“PostgreSQL_”開頭的博文。 1 數據回退 上一節“PostgreSQL_數據下載并…

golang單機鎖實現

1、鎖的概念引入 首先&#xff0c;為什么需要鎖&#xff1f; 在并發編程中&#xff0c;多個線程或進程可能同時訪問和修改同一個共享資源&#xff08;例如變量、數據結構、文件&#xff09;等&#xff0c;若不引入合適的同步機制&#xff0c;會引發以下問題&#xff1a; 數據競…

【HarmonyOS Next】鴻蒙應用實現彈框DialogHub詳解

【HarmonyOS Next】鴻蒙應用實現彈框DialogHub詳解 一、前言 鴻蒙中實現彈框目前官方提供openCustomDialog和CustomDialog兩種模式。推薦前者&#xff0c;詳情見下圖和官網文檔鏈接&#xff1a; https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V14/arkts-u…

機器學習算法實戰——天氣數據分析(主頁有源碼)

?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連? ? ??? 1. 引言 天氣數據分析是氣象學和數據科學交叉領域的一個重要研究方向。隨著大數據技術的發展&#xff0c;氣象數據的采集、存儲和分…

輸電線路專業英語詞匯

輸電線路transmission line 雙回路double circuit 導線conductor 地線ground &#xff08;Earth&#xff09;wire 雙回路耐張塔double-circuit tension towers 直線塔tangent tower 地質Geological 水文Hydrological 塔位坐標Coordinate of Tower Location 轉角塔angle tower 直…

炫酷的3D按鈕效果實現 - CSS3高級特性應用

炫酷的3D按鈕效果實現 - CSS3高級特性應用 這里寫目錄標題 炫酷的3D按鈕效果實現 - CSS3高級特性應用項目介紹核心技術實現1. 基礎結構設計2. 視覺效果實現2.1 背景漸變2.2 立體感營造 3. 交互動效設計3.1 懸停效果3.2 按壓效果 技術要點分析1. 深度層次感2. 動畫過渡3. 性能優…

解決python配置文件類configparser.ConfigParser,插入、讀取數據,自動轉為小寫的問題

配置類 [Section1] Key_AAA Value[Section2] AnotherKey Value默認情況下&#xff0c;ConfigParser會將ini配置文件中的KEY&#xff0c;轉為小寫。 重載后配置類&#xff1a; 繼承類從configparser.ConfigParser改為configparser.RawConfigParser重載方法optionxform&#…

微服務的網關配置

微服務的網關配置 1. 網關路由 1.1 網關 1.1.1 存在問題 單體架構時我們只需要完成一次用戶登錄、身份校驗&#xff0c;就可以在所有業務中獲取到用戶信息。而微服務拆分后&#xff0c;每個微服務都獨立部署&#xff0c;這就存在一些問題&#xff1a;每個微服務都需要編寫身…

【硬核實戰】ETCD+AI智能調度深度整合!從架構設計到調優避坑,手把手教你打造高可用調度系統!

一、核心架構設計&#xff1a;ETCD如何賦能AI調度&#xff1f; &#x1f525; 架構圖&#xff1a; [AI調度引擎] ← 實時數據 → [ETCD集群] ↓ 決策指令 [執行層&#xff08;車輛/物流/交通設備&#xff09;] 核心角色&#xff1a; ETCD&#xff1a;存儲調度策略、節點狀…

區間震蕩指標

區間震蕩指標的邏輯如下&#xff1a; 一、函數注解 1. Summation函數 功能&#xff1a; 計算給定價格序列Price的前Length個數據點的和&#xff0c;或在數據點數量超過Length時&#xff0c;計算滾動窗口內的價格和。 參數&#xff1a; Price(1)&#xff1a;價格序列&#…