前言
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。
這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。
這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
定義
單例模式: 一個類有且只有一個實例,且提供一個全局訪問方法來訪問這個實例。
注:1、被static修飾的變量成爲靜態變量,當類加載器將類加載到JVM中的時候就會創建靜態變量,這跟對象是否創建無關。
2、靜態代碼塊:靜態代碼塊的代碼只會在類第一次初始化的時候執行一次。
3、靜態變量創建後,會一直放在內存中,只有進程被殺掉時,纔會被清空。這也是單例模式的核心。
單利模式
1、餓漢模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public Singleton getInstance() {
return instance;
}
}
缺點是在類加載的時候就實例化了對象,有點浪費空間,優點是線程安全,因爲static變量會在類裝載的時候初始化,並且多個實例的static變量會共享一塊內存區域。
2、懶漢模式
public class Singleton{
private static Singleton singleton= null;
private Singleton(){
}
public Singleton getInstance(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
但是由於線程不安全,於是加上了synchronized關鍵字修飾方法
public class Singleton{
private static Singleton singleton= null;
private Singleton(){
}
public synchronized Singleton getInstance(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
加上鎖以後又影響性能,於是又有了雙鎖檢測(DCL)版本的單例模式(雙重檢查+鎖)
public class Singleton {
private static volitile Singleton singleton = null;
private Singleton() {
}
public Singleton getInstance() {
if (singleton == null) {
synchronized (this) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
1.爲什麼要進行第一次判空
如果說我們沒有第一次驗校,每一個線程都要走synchronized 中的代碼,而每一次線程都要去拿到同步鎖才能執行。在多線程的情況下每一個線程要拿到single 對象都要排隊等待同步鎖釋放。因此第一次驗校作用就是爲了提高程序的效率。
2.爲什麼要進行第二次判空
如果有兩個線程A和B都通過了第一次判空,進入到獲取鎖階段A拿到鎖進行對象創建後釋放鎖,此時B就不需要在創建對象,所以再次判斷不爲空,直接返回。
3.變量爲什麼要加volatile關鍵字
volatile防止指令重排序,single = new Single() 在我們看來就是一句話操作而已,但在虛擬機看來它一共分爲了幾個指令操作:
- 爲對象分配內存空間
- 初始化對象
- 將引用指向對象的內存空間地址
假如線程A執行 single = new Single()虛擬機是按132排序執行,當執行到3的時候single 引用已經不爲空。此時若線程B執行到第一次驗校處(第一次驗校不在同步代碼中,因此所有線程隨時都可以訪問),它判斷 single ==null 得到false,直接返回single對象。但是此時single對象還沒初始化完成,因此很有可能就會發生bug。