緒論
設計模式分爲三種類型,共 23 種
- 創建型模式:單例模式、抽象工廠模式、原型模式、建造者模式、工廠模式。
- 結構型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
- 行爲型模式:模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、 解釋器模式(Interpreter 模式)、狀態模式、策略模式、職責鏈模式(責任鏈模式)。
單例模式
1、介紹
所謂類的單例設計模式,就是採取一定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法(靜態方法)。
比如 Hibernate 的 SessionFactory,它充當數據存儲源的代理,並負責創建Session 對象。SessionFactory 並不是 輕量級的,一般情況下,一個項目通常只需要一個 SessionFactory 就夠,這是就會使用到單例模式。
2、單例設計模式八種方式
1. 餓漢式(靜態常量)
2.餓漢式(靜態代碼塊)
3.懶漢式(線程不安全)
4.懶漢式(線程安全,同步方法)
5.懶漢式(同步代碼塊)
6.雙重檢查
7.靜態內部類
8.枚舉
2.1 餓漢式(靜態常量)
步驟如下:
- 構造器私有化 (防止 new )
- 類的內部創建對象
- 向外暴露一個靜態的公共方法。getInstance
- 代碼實現
/**
* 單例模式之 餓漢式(靜態變量)
* 缺點,可能並沒有用到這個類,浪費內存。
*/
public class Singleton {
private Singleton(){
}
private final static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
優缺點說明:
- 優點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。
- 缺點:在類裝載的時候就完成實例化,沒有達到LazyLoading的效果。如果從始至終從未使用過這個實例,則
會造成內存的浪費 - 這種方式基於classloder機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,在單例模式中大 多數都是調用 getInstance 方法,但是導致類裝載的原因有很多種,因此不能確定有其他的方式(或者其他的靜 態方法)導致類裝載,這時候初始化 instance 就沒有達到 lazy loading 的效果
- 結論:這種單例模式可用,可能造成內存浪費。
2.2 餓漢式(靜態代碼塊)
/**
* 單例模式之 餓漢式(靜態代碼塊)
* 缺點,可能並沒有用到這個類,浪費內存。
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
static {
singleton = new Singleton();
}
public static Singleton getInstance() {
return singleton;
}
}
這種方式和第一種方式類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執 行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。
2.3 懶漢式(線程不安全)
/**
* 單例模式之 懶漢式(靜態方法)
* 缺點,存在線程安全問題,不推薦使用
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
//當處於多線程的情況下,幾個線程同時進來,那就失去了單例的效用了。
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.4 懶漢式(線程安全,同步方法)
/**
* 單例模式之 懶漢式(加鎖)
* 缺點,結局了線程安全的問題,但是每次調用都要進行一次同步判斷,很影響效率,不推薦!
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.5 懶漢式(同步代碼塊)
/**
* 單例模式之 懶漢式(代碼塊加鎖)
* 這種方式並沒有起到任何的效果,多線程情況下還是會存在多個對象的情況。
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
/**
* 當線程a, b, c進來的時候,singleton都是null,所以都進到判斷裏面。
* 由於加了鎖,所以a先進去實例對象後,b和c在外面等着,a出去以後鎖釋放,b和c先後又繼續實例對象了。
* 所以這種方式並不能做到單例!!!
**/
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
2.6 雙重檢查
/**
* 單例模式之 懶漢式(雙重檢查)
* 這種方式推薦使用的。
*/
public class Singleton {
private static volatile Singleton singleton; // volatile 數據修改後做一個即時的更新
private Singleton(){
}
/**
* 當線程a, b, c進來的時候,singleton都是null,所以都進到判斷裏面。
* 由於加了鎖,所以a先進去實例對象後,b和c在外面等着。
* a出去以後鎖釋放,b和c先後進去,但是因爲此時singleton已經不是null了,所以直接出去了。
* 而下次再次調用這個方法的時候,在第一重判斷就被攔住了,不用進行同步,又保證了效率。
**/
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
2.7 靜態內部類
/**
* 單例模式之 靜態內部類
* 好處:
* 1、當一個類在加載的時候,靜態內部類是不會被立即加載的。
* 2、類在加載的過程中是線程安全的。
* 這種方式推薦使用的。
*/
public class Singleton {
private Singleton(){
}
private static class InnerSingleton {
private static final Singleton INSTANCE = new Singleton();
}
/**
* 這時候纔去初始化靜態內部類
**/
public static Singleton getInstance() {
return InnerSingleton.INSTANCE;
}
}
2.8 枚舉
/**
* 單例模式之 枚舉
* 避免了多線程同步問題
* 這種方式推薦使用的。
*/
public class Singleton {
public static void main(String[] args) {
SingletonEnum singletonEnum = SingletonEnum.INSTANCE;
SingletonEnum singletonEnum2 = SingletonEnum.INSTANCE;
// singletonEnum.sayHello();
System.out.println(singletonEnum == singletonEnum2); //true 表示兩個對象相同
}
}
enum SingletonEnum {
INSTANCE;
public void sayHello() {
System.out.println("hello~");
}
}