文章目錄
- 單例模式(懶漢式)
- 代碼
- 懶漢式(線程不安全)
- 懶漢式(線程安全,加鎖)
- 雙重檢查鎖(線程安全,推薦)
單例模式(懶漢式)
懶漢式是符合懶加載的模式,但是會存在線程并發的問題發生,所以還需要一種解決線程并發的機制,比如:加鎖等
單例模式懶漢式主要的構成是如下
- 單例類
- 私有化構造函數(防止實例化)
- 私有化變量
- 公共靜態獲得實例的方法(在調用這個方法的時候才進行實例化)
- 解決線程并發的機制
代碼
懶漢式(線程不安全)
package singleton.type3;/***** @author: Hui**/
public class SingletonTest3 {public static void main(String[] args) {Singleton singleton = Singleton.getInstance();Singleton singleton1 = Singleton.getInstance();System.out.println(singleton == singleton1);System.out.println(singleton.hashCode());System.out.println(singleton1.hashCode());}
}class Singleton{//1.構造器私有化private Singleton(){}//2.靜態實例私有化private static Singleton singleton;//3.提供實例的靜態方法public static Singleton getInstance(){if (singleton == null){singleton = new Singleton();}return singleton;}
}
以上方法雖然實現了懶加載,但是線程不安全,在實際開發情況下不能使用。
因為以上代碼會出現線程不安全的情況,那么我們如何保證線程安全呢?最簡單的就是加鎖,在提供實例的靜態方法中加鎖,就可以保證線程安全了。
懶漢式(線程安全,加鎖)
package singleton.type4;
/*** @author: Hui**/
public class SingletonTest4 {public static void main(String[] args) {System.out.println("線程安全,加鎖");Singleton singleton = Singleton.getInstance();Singleton singleton1 = Singleton.getInstance();System.out.println(singleton == singleton1);System.out.println(singleton.hashCode());System.out.println(singleton1.hashCode());}
}class Singleton {//1.構造器私有化private Singleton() {}//2.靜態實例私有化private static Singleton singleton;//3.提供實例的靜態方法public static synchronized Singleton getInstance() {if (singleton == null) {singleton = new Singleton();}return singleton;}
}
雖然解決了線程安全問題,但是性能太差了,每一次調用實例都需要進入同步方法,其實我們創建實例的時候保持同步就可以了。這個方法因為性能比較差,開發過程中不建議使用。
那上述鎖的是一個方法,我們可不可以將鎖的顆度降低,鎖住一個代碼塊呢?
代碼如下
//3.提供實例的靜態方法public static synchronized Singleton getInstance() {if (singleton == null) {synchronized(Singleton){singleton = new Singleton();}}return singleton;}
像以上方法是不是就實現了提高性能呢?是的,但是也出現了線程不安全的問題,比如有多個線程進入 if (singleton == null) 那么是不是會有多個線程進行創建對象。所以為了減少顆粒度又要保證線程安全,我們可以使用雙層檢查鎖來完成!!!
//3.提供實例的靜態方法public static synchronized Singleton getInstance() {if (singleton == null) {synchronized(Singleton.class){if (singleton == null){singleton = new Singleton();} }}return singleton;}
我們只需要在加鎖的代碼塊中再進行一次空判斷,就可以很好的解決并發問題了,當一個線程進入的時候,先判斷是否為空,為空才創建對象,不為空直接返回。
雙重檢查鎖很好的解決線程不安全問題,并且達到性能的提升。
在實際開發中推薦使用
雙重檢查鎖(線程安全,推薦)
package singleton.type5;/*** @author: Hui**/
public class SingletonTest5 {public static void main(String[] args) {System.out.println("雙重檢查鎖,推薦使用");Singleton singleton = Singleton.getInstance();Singleton singleton1 = Singleton.getInstance();System.out.println(singleton == singleton1);System.out.println(singleton.hashCode());System.out.println(singleton1.hashCode());}
}class Singleton {//1.私有化構造器private Singleton() {}//2.聲明靜態變量,volatile 保證內存的可見性private static volatile Singleton instance;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {//雙重檢查鎖if (instance == null) {instance = new Singleton();}}}return instance;}}
注意變量一定要使用 volatile 修飾,保證變量內存修改時的可見性。