你真的理解了java單例模式嗎?講別人都忽略的細節

2020-12-27 老劉情感影視

前言:老劉這篇文章敢做保證,java的單例模式講的比大多數的技術博客都要好,講述別人技術博客都沒有的細節!!!

1 java單例模式

直接講實現單例模式的兩種方法:懶漢式和餓漢式,單例模式的概念自己上網搜吧這裡就不講了!

這裡會涉及到java中的jvm,如果你沒有這方面的知識,我建議你先去補補,不然會有點迷糊!

首先說一下什麼時候進行類加載?

java虛擬機沒有進行強制性的約束,但是對於初始化卻嚴格規定了有且只有4種情況必須先對類進行初始化。

我們要知道的是在類加載的過程中,加載、驗證、準備是在初始化之前完成的,所以進行了初始化,加載、驗證、準備自然就在之前完成了。

然後這四種情況是分別遇到 new 、 getstatic 、 putstatic 和 invokestatic 這四條指令時,如果對應的類沒有初始化,則要對對應的類先進行初始化。

講完類加載時機,就可以講懶漢式和餓漢式了。

直接先說說懶漢式為什麼是線程不安全的?

先看最開始的代碼:

public class Student2 { //1:構造私有 private Student2(){} //2:定義私有靜態成員變量,先不初始化 private static Student2 student = null; //3:定義公開靜態方法,獲取本身對象 public static Student2 getSingletonInstance(){ //沒有對象,再去創建 if (student == null) { student = new Student2(); } //有對象就返回已有對象 return student; } }結合之前講的類加載內容,遇到new或加載靜態方法了就會進行類加載了。

線程1它new了一個對象,線程2它緊接著也new一個對象,第二個對象的值把第一個對象的值覆蓋了,不管new了多少個對象,都會產生垃圾對象,只有最後一個對象才會保持住,其他對象都會變成不可達對象,被垃圾回收,這個過程就相當於產生了大量無效對象,這就是線程不安全的原因。

那為了讓懶漢式變得線程安全,我們要怎麼做?

看代碼:

public class Student4 { private volatile static Student4 student = null; private Student4() {} public static Student4 getSingletonInstance() { if (student == null) {//第一個null判斷,是先大範圍過濾一遍 synchronized (Student4.class) { if (student == null) { student = new Student4(); } } } return student; }}這個叫雙重檢查鎖DCL,第一個if先大範圍判斷是不是空值,經過synchronized,線程1先進去執行完後,線程2才能進去,然後第二個if判斷是否完成創建類的實例,線程1創建完了,線程2就不用創建了。

那為什麼要加volatile關鍵字呢?

因為我們Student student = new Student()的執行過程是:

1、new觸發類加載機制(已經被加載過的類不需要再次加載)

2、分配內存空間

3、將對象進行初始化4、將對象引用地址賦值給棧空間中的變量但我們JVM中的JIT即時編輯器會對代碼的執行過程進行優化,把過程變為1、2、4、3。

這是什麼意思呢?就是未經初始化直接賦值,這樣就是student直接有值了,但整個對象還未初始化完成,所以這個對象是不完整的,是個未成品。在JVM規範中,它是一個根本不能用的對象。

到了這個時候,線程1做了這麼多事,我們讓它休息會,給CPU稍微停一下,線程2就來了,它就直接得到了對象,但它調用對象的方法時,就會報錯。雖然這個對象有值,但還未初始化完成。所以我們要加上volatile關鍵字禁止指令重新排序。

面試重災區說的差不多了,餓漢式還是要講講。

最後就說說餓漢式為什麼沒有線程安全問題?

看代碼:

public class Student1 { // 2:成員變量初始化本身對象 private static Student1 student = new Student1(); // 構造私有 private Student1() { } // 3:對外提供公共方法獲取對象 public static Student1 getSingletonInstance() { return student; } public void sayHello(String name) { System.out.println("hello," + name); }}根據類加載的東西,在多線程的條件下,線程1先執行getSingletonInstance()時,就會進行類加載,類的靜態資源就會進行初始化。根據JVM安全機制裡說的,當一個類被JVM加載的時候,該類的加載是線程安全的,相當於JVM對該過程加鎖了。

所以整個過程處於一個鎖的範圍內,然後靜態成員變量進行初始化就相當於Student1()被new了,只會被new一次。

當第二個線程進來,它就發現這個類已經被加載了,就不需要進行加載了,對象也不需要頻繁創建,所以線程是安全的!

2 總結

老劉看過很多關於java單例模式的資料,多多少少都會缺少一點細節,這次老劉把它補全了。

如果覺得寫的不錯,給老劉點個讚!

相關焦點

  • Java的單例模式
    一、什麼是單例模式?
  • Java單例模式深入詳解
    我主要是應用在使用普通類模擬枚舉類型裡,後來發現這就是傳說中的單例模式。構造函數弄成private 就是單例模式,即不想讓別人用new 方法來創建多個對象,可以在類裡面先生成一個對象,然後寫一個public static方法把這個對象return出去。
  • 為什麼要用單例模式?
    我們在編程中最常用的模式就是單例模式了,然而單例模式都用在什麼場合?為什麼不用靜態方法而要用單例模式呢?
  • java設計模式中的單例模式,收藏起來慢慢看!
    在java中,單例模式算是比較基礎和簡單的,今天就來簡單聊聊什麼是單例模式。比如說,一個應用程式中,某個類的實例對象只有一個,而我們沒有辦法new,因為構造器已經被private聲明了,通過getInstance()的方法可以獲取它們的實例。
  • 深入解析單例模式的七種實現
    來源:http://t.cn/E4J3ffX什麼是單例模式如何實現單例模式呢?單例模式的七種實現總結什麼是單例模式什麼是單例模式呢?Show me the code讓我們來看看單例模式的7種實現方式單例模式的七種實現第一種:懶漢式加載懶漢式加載:最簡單的單例模式:2步,1.把自己的構造方法設置為私有的,不讓別人訪問你的實例,2.提供一個static方法給別人獲取你的實例.
  • 設計模式一:單例模式
    什麼是單例模式單例模式是指系統中的某個類只能有一個對象實例。
  • 再見面試官:單例模式有幾種寫法?
    「「你知道茴香豆的『茴』字有幾種寫法嗎?」糾結單例模式有幾種寫法有用嗎?有點用,面試中經常選擇其中一種或幾種寫法作為話頭,考查設計模式和coding style的同時,還很容易擴展到其他問題。這裡講解幾種筆者常用的寫法,但切忌生搬硬套,去記「茴香豆的寫法」。
  • 高並發下線程安全的單例模式(最全最經典,值得收藏)
    在所有的設計模式中,單例模式是我們在項目開發中最為常見的設計模式之一,而單例模式有很多種實現方式,你是否都了解呢?高並發下如何保證單例模式的線程安全性呢?如何保證序列化後的單例對象在反序列化後任然是單例的呢?這些問題在看了本文之後都會一一的告訴你答案,趕快來閱讀吧!什麼是單例模式?
  • 單例(Singleton)設計模式及在Java中的實現
    設計模式(Design Pattern)是在經過大量的實踐後總結出來,並且經過理論化分析優選出的編程風格、代碼結構以及解決問題的思考方式。一、何為單例模式?在編程世界中,目前經典的共有23種設計模式,單例模式只是其中一種。
  • Android設計模式(1)——單例模式
    單例模式的UML類圖 實現單例模式主要有如下幾個關鍵點:構造函數不對外開放,一般為Private通過一個靜態方法或者枚舉返回單例對象確保單例類的對象有且只有一個,尤其在多線程環境下確保單例對象在反序列化時不會重新構建對象示例示例類圖:  示例實現代碼//普通員工類public class Staff
  • Java 實現單例模式的 9 種方法
    什麼是單例模式二. 單例模式的特點三. 單例模式VS靜態類四. 單例模式的實現一. 什麼是單例模式因進程需要,有時我們只需要某個類同時保留一個對象,不希望有更多對象,此時,我們則應考慮單例模式的設計。二. 單例模式的特點單例模式只能有一個實例。單例類必須創建自己的唯一實例。
  • 細思極恐,你真的會寫java嗎
    原文地址:http://lrwinx.github.io/2017/03/04/細思極恐-你真的會寫java嗎/作者: Lrwin導語自2013年畢業後,今年已經是我工作的第4個年頭了,總在做java相關的工作,終於有時間坐下來,寫一篇關於java寫法的一篇文章,來探討一下如果你真的是一個java程式設計師,那你真的會寫java嗎?
  • 單例模式襲來
    吹一波單例模式❝確保某個類只有一個實例,而且自行實例化並向整個系統提供整個實例。說白了,就是"獨苗"!什麼是"自行實例化"?就是整個實例是在類內部自己創建的,不是在別的類中創建的。❞單例誰都會寫,而且五花八門,且隨洒家來瞅瞅。
  • 設計模式大冒險第四關:單例模式,如何成為你的「唯一」
    這一篇文章是關於設計模式大冒險系列的第四篇文章,這一系列的每一篇文章我都希望能夠通過通俗易懂的語言描述或者日常生活中的小例子來幫助大家理解好每一種設計模式。今天這篇文章來跟大家一起學習一下單例模式。相信讀完這篇文章之後,你肯定會有所收穫的。關於單例模式,這應該是設計模式中最簡單的一種了。
  • 細思極恐 —— 你真的會寫 Java 嗎?
    相關的工作,終於有時間坐下來,寫一篇關於java寫法的一篇文章,來探討一下如果你真的是一個java程式設計師,那你真的會寫java嗎?技術點本文不是一個吹噓的文章,不會講很多高深的架構,相反,會講解很多基礎的問題和寫法問題,如果讀者自認為基礎問題和寫法問題都是不是問題,那請忽略這篇文章,節省出時間去做一些有意義的事情。
  • 細思極恐-你真的會寫java嗎?
    來探討一下如果你真的是一個java程式設計師,那你真的會寫java嗎?技術點本文不是一個吹噓的文章,不會講很多高深的架構,相反,會講解很多基礎的問題和寫法問題,如果讀者自認為基礎問題和寫法問題都不是問題,那請忽略這篇文章,節省出時間去做一些有意義的事情。
  • Java 連接 Redis 工具包之 Jedis 的單例模式用法,非常好用哦~
    為了避免使用時每次都要連接redis,可以使用單例模式,下面介紹這種模式的編程方法,先下載 Jedis 工具包(下載網址見附錄)。1、單例模式的服務類import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig
  • 細思極恐-你真的會寫Java嗎?
    連結:http://lrwinx.github.io/2017/03/04/細思極恐-你真的會寫java嗎/ 自2013年畢業後,今年已經是我工作的第4個年頭了,總在做Java相關的工作,終於有時間坐下來,寫一篇關於Java寫法的一篇文章,來探討一下如果你真的是一個Java程式設計師,那你真的會寫Java嗎?
  • Java典型面試題 ——談談你對Java平臺的理解?
    出處 | 極客時間《Java 核心技術 36 講》  從你接觸 Java 開發到現在,你對 Java 最直觀的印象是什麼呢?是它宣傳的 「Write once, run anywhere」,還是目前看已經有些過於形式主義的語法呢?你對於 Java 平臺到底了解到什麼程度?請你先停下來總結思考一下。
  • Java Singleton設計模式DEMO
    java.awt.Desktop:Desktop該類允許Java應用程式使用在本機桌面上註冊的應用程式(如用戶的默認瀏覽器或郵件客戶端)啟動URI或文件。本機桌面和相關應用程式是獨一無二的。所以必須只有一個Desktop類的實例。單例設計模式的實現你如何確保一個類只有一個實例?好吧,在Java中有幾種方法可以做到這一點。