面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

来源:https://www.cnblogs.com/yunxi520/archive/2020/05/11/12871462.html
-Advertisement-
Play Games

單例模式,是Java中比較常見的一個設計模式,也是我在面試時經常會問到的一個問題。 經過我的初步統計,基本上有60%左右的人可以說出2-4種單例的實現方式,有40%左右的人可以說出5-6種單例的實現方式,只有20%左右的人能夠說出7種單例的實現。 而只有不到1%的人能夠說出7種以上的單例實現。 其實 ...


單例模式,是Java中比較常見的一個設計模式,也是我在面試時經常會問到的一個問題。

 

經過我的初步統計,基本上有60%左右的人可以說出2-4種單例的實現方式,有40%左右的人可以說出5-6種單例的實現方式,只有20%左右的人能夠說出7種單例的實現。

 

而只有不到1%的人能夠說出7種以上的單例實現。

 

其實,作為面試官,我大多數情況下之所以問單例模式,是因為這個題目可以問到很多知識點。

 

比如線程安全、類載入機制、synchronized的原理、volatile的原理、指令重排與記憶體屏障、枚舉的實現、反射與單例模式、序列化如何破壞單例、CAS、CAS的ABA問題、Threadlocal等知識。

 

一般情況下,只需要從單例開始問起,大概就可以完成一場面試的整個流程,把我想問的東西都問完,可以比較全面的瞭解一個面試者的水平。

 

以下,是一次面試現場的還原,從單例模式開始:

 

Q:你知道怎麼不使用synchronized和lock實現一個線程安全的單例嗎?

A:我知道,可以使用"靜態內部類"實現。

 

靜態內部類實現單例模式:

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

Q:除了靜態內部類還會其他的方式嗎?

A:還有就是兩種餓漢模式。

 

餓漢實現單例模式:

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

 

餓漢變種實現單例模式:

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

Q:那你上面提到的幾種都是線程安全的嗎?

A:是線程安全的

Q:那是如何做到線程安全的呢?

A:應該是因為我使用了static,然後類載入的時候就線程安全了吧?

Q:其實你說的並不完全對,因為以上幾種雖然沒有直接使用synchronized,但是也是間接用到了。

(這裡面根據回答情況會朝兩個不同的方向展開:1、類載入機制、模塊化等;2、繼續深入問單例模式)

 

類載入過程的線程安全性保證

以上的靜態內部類、餓漢等模式均是通過定義靜態的成員變數,以保證單例對象可以在類初始化的過程中被實例化。

 

這其實是利用了ClassLoader的線程安全機制。ClassLoader的loadClass方法在載入類的時候使用了synchronized關鍵字。

所以, 除非被重寫,這個方法預設在整個裝載過程中都是線程安全的。所以在類載入過程中對象的創建也是線程安全的。

 

Q:那還回到剛開始的問題,你知道怎麼不使用synchronized和lock實現一個線程安全的單例嗎?

(並不是故意窮追不捨,而是希望能可以引發麵試者的更多思考)

A:額、、、那枚舉吧,枚舉也可以實現單例。

 

枚舉實現單例模式:

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

Q:那你知道枚舉單例的原理嗎?如何保證線程安全的呢?

 

枚舉單例的線程安全問題

枚舉其實底層是依賴Enum類實現的,這個類的成員變數都是static類型的,並且在靜態代碼塊中實例化的,和餓漢有點像, 所以他天然是線程安全的。

 

Q:所以,枚舉其實也是藉助了synchronized的,那你知道哪種方式可以完全不使用synchronized的嗎?

A:en....我想想

Q:(過了一會他好像沒有思路)你知道CAS嗎?使用CAS可以實現單例嗎?

(面試中,如果面試者對於鎖比較瞭解的話,那我大多數情況下都會繼續朝兩個方向深入問:1、鎖的實現原理;2、非鎖,如CAS、ThreadLocal等)

A:哦,我知道,CAS是一項樂觀鎖技術,當多個線程嘗試使用CAS同時更新一個變數時,只有其中一個線程能更新成功。

 

藉助CAS(AtomicReference)實現單例模式:

public class Singleton {

private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

private Singleton() {}

public static Singleton getInstance() {

for (;;) {

Singleton singleton = INSTANCE.get();

if (null != singleton) {

return singleton;

}

singleton = new Singleton();

if (INSTANCE.compareAndSet(null, singleton)) {

return singleton;

}

}

}

}

Q:使用CAS實現的單例有沒有什麼優缺點呀?

A:用CAS的好處在於不需要使用傳統的鎖機制來保證線程安全,CAS是一種基於忙等待的演算法,依賴底層硬體的實現,相對於鎖它沒有線程切換和阻塞的額外消耗,可以支持較大的並行度。

Q:你說的好像是優點?那缺點呢?

 

CAS實現的單例的缺點

CAS的一個重要缺點在於如果忙等待一直執行不成功(一直在死迴圈中),會對CPU造成較大的執行開銷。

 

另外,代碼中,如果N個線程同時執行到 singleton = new Singleton();的時候,會有大量對象被創建,可能導致記憶體溢出。

Q:好的,除了使用CAS以外,你還知道有什麼辦法可以不使用synchronized實現單例嗎?

A:這回真的不太知道了。

Q:(那我再提醒他一下吧)可以考慮下ThreadLocal,看看能不能實現?

(面試者沒有思路的時候,我幾乎都會先做一下提醒,實在沒有思路再換下一個問題)

A:ThreadLocal?這也可以嗎?

Q:你先說下你理解的ThreadLocal是什麼吧

(通過他的回答,貌似對這個思路有些疑惑,不著急。先問一個簡單的問題,讓面試者放鬆一下,找找自信,然後再繼續問)

 

ThreadLoacal

ThreadLocal會為每一個線程提供一個獨立的變數副本,從而隔離了多個線程對數據的訪問衝突。對於多線程資源共用的問題,同步機制(synchronized)採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式。

 

同步機制僅提供一份變數,讓不同的線程排隊訪問,而ThreadLocal為每一個線程都提供了一份變數,因此可以同時訪問而互不影響。

Q:那理論上是不是可以使用ThreadLocal來實現單例呢?

A:應該也是可行的。

 

使用ThreadLocal實現單例模式:

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

Q:嗯嗯,好的,那有關單例模式的實現的問題我就問的差不多了。

(ThreadLocal這種寫法主要是考察面試者對於ThreadLocal的理解,以及是否可以把知識活學活用,但是實際上,這種所謂的"單例",其實失去了單例的意義...)

(但是說實話,能回答到這一題的人很少,大多數面試者基本上在前面幾道題就已經沒有思路了,大多數情況下根本不會問到這個問題就要改方向了)

A:(心中竊喜)嗯嗯,學習到很多,感謝

Q:那...你知道如何破壞單例嗎?

(單例問題,必問的一個。通過這個引申到序列化和反射的相關知識)

A:(額....)

最後這裡小編整理了一套面試資料,讓你面試不慌張

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

面試官有毒吧?讓實現線程安全的單例,又不讓使用synchronized

 

領取步驟:
1、加微信即可免費領取


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 近來開始學Go,留此博客以記錄學習過程,順便鞭策自己更加努力。 簡單介紹 "The Go Programming Language" Go(又稱Golang)是Google開發的一種靜態強類型、編譯型、併發型,並具有垃圾回收功能的編程語言。 我學習主要參考七米老師的博客 "李文周的博客" 以及他在B ...
  • 1.break break用來強行退出迴圈結構或者switch結構,不執行迴圈中剩餘的語句。 例:(測試1-10隨機幾次可以隨機到6) while(true){ count++; int a=(int)(10*Math.random()); if(a==6){ break; } } System.o ...
  • 1、添加依賴 2、編碼工具類 3、測試模板引擎 控制台輸出 相關文檔 "Thymeleaf 模板語法" ...
  • 在迴圈語句中,再嵌套一個或多個迴圈,稱位迴圈嵌套 用幾個由淺入深的例子來瞭解迴圈嵌套: 1.輸出一個3*3的矩陣 for(int i = 0;i<3;i++){ for(int j = 0;j<3;j++){ System.out.print(" * "); } System.out.println ...
  • 一、Swagger 1、什麼是 Swagger ? Swagger 是一個規範和完整的框架,用於生成、描述、調用以及可視化的 Restful 風格的 Web 服務。 簡單的理解:是一款 REST API 文檔生成工具,生成線上的介面文檔,方便介面測試。 2、為什麼使用 Swagger? 前後端分離開 ...
  • 迴圈結構有三個:while型迴圈、for型迴圈、do-while型迴圈 while型迴圈和for型迴圈都屬於當型迴圈,do-while型迴圈屬於直到型迴圈(少見)。 1.while型迴圈for型迴圈 結構: while(【布爾表達式】){ 【迴圈語句】; } 先運行【布爾表達式】,如果其值為真,執行 ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 時間序列 1、時間序列圖 時間序列圖用於可視化給定指標如何隨時間變化。在這裡,您可以瞭解1949年至1969年之間的航空客運流量如何變化。 # Import Data df ...
  • Spring 框架的核心特性是可以用於開發任何 Java 應用程式,但是在 Java EE 平臺上構建 web 應用程式是需要擴展的。 Spring 框架的目標是使 J2EE 開發變得更容易使用。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...