天天看點

給女友講講設計模式——單例模式(JAVA執行個體)2

前言

曾經有兩個人,一個是A,一個是B,他們倆都是這個世界上獨立的個體,每個人都是這個世界上獨一無二的。這個A呢,在一出生的時候,别人就會把他需要的食物全部給他,然後等到他餓了的時候,就可以直接拿過來吃,但是在他不餓的時候,天天被在身上确是一種負擔,人們都管他叫做餓漢。B呢,則是在出生的時候,并沒有任何食物給他,但是在他餓的時候,他連伸手都不用,隻需要張嘴喊一嗓子,便有人給他送來了食物,就因為他這麼懶,是以被叫做懶漢。為什麼要講這個故事呢,接下來你就會明白了。

單例模式

在web開發中,我們經常碰到這樣一種情況,我們在整個項目的上下文中,有且隻有一個執行個體,所有線程操縱的都是它,例如說Mybatis中的SqlSessionFactory。如果說隻存在一個執行個體,那麼他絕對是不可以new出來,是以他的構造方法一定是私有的。

單例模式從方式上分為懶漢模式,和餓漢模式,這就像是咱們剛剛提到故事裡的B和A一樣,具體代碼如下:

package singleton;

/**
 * 這是懶漢模式,但是對于多線程的情況下是不安全的
 * 
 * @author luckyharry
 *
 */
public class SingletonLazyNotSafe {
    private SingletonLazyNotSafe() {
        System.out.println("單例懶漢模式,無鎖,執行個體化--");
    }

    private static SingletonLazyNotSafe singletonLazyNotSafe;

    public static SingletonLazyNotSafe getInstance() {
        if (singletonLazyNotSafe == null) {
            singletonLazyNotSafe = new SingletonLazyNotSafe();
        }
        return singletonLazyNotSafe;
    }
}
           

這是懶漢模式,但是我們會發現getInstance()方法,多個線程可能會共同競争,将會導緻資料的不統一性,那麼我們需要給它添加一個鎖,關鍵字synchronized。

package singleton;

/**
 * 這是懶漢模式,對于多線程的情況下是安全的 但是因為加了鎖,是以效率比較低
 * 
 * @author luckyharry
 *
 */
public class SingletonLazySafe {
    private SingletonLazySafe() {
        System.out.println("懶漢模式 有鎖 執行個體化--");
    }

    private static SingletonLazySafe singletonLazySafe;

    public static synchronized SingletonLazySafe getInstance() {
        if (singletonLazySafe == null) {
            singletonLazySafe = new SingletonLazySafe();
        }
        return singletonLazySafe;
    }
}
           

懶漢模式,他是在當需要獲得的時候才執行個體化,就像是剛剛所說的B那個人一樣,實作了懶加載。但是加了鎖之後,效率比較低下。

package singleton;

public class SingletonHunger {

    private static SingletonHunger singletonHunger=new SingletonHunger();
    // 構造器為私有的,不可以通過new實力化
    private SingletonHunger() {
        System.out.println("初始化 Singleton 餓漢方式--");
    }


    public static SingletonHunger getInstance() {
        return singletonHunger;
    }
}
           

餓漢模式我們不再去考慮多線程的問題,但是它卻是在類加載的時候便會執行個體化單例本身,并未實作懶加載。這就像是剛剛說的A一樣,在一出生的時候,該執行個體化的已經被執行個體化放在那了。

package singleton;

public class SingletonInnerClass {

    // 構造器為私有的,不可以通過new實力化
    private SingletonInnerClass() {
        System.out.println("初始化 Singleton 内部類方式--");
    }

    private static class SingletonCreate {
        private static SingletonInnerClass singleton = new SingletonInnerClass();
    }

    public static SingletonInnerClass getInstance() {
        return SingletonCreate.singleton;
    }
}
           

這種内部類的形式,既解決了鎖的問題,又實作了懶加載,是一種懶漢模式的變種。

接下來就是測試類了。

package singleton;

/**
 * 懶漢模式以及餓漢模式的本質差別在于
 * 單例的類的初始化的位置,如果我們是在類剛開始初始化的時候就初始化了的化,那麼是餓漢加載
 * 如果我們是在擷取執行個體的那個方法初始化的化(實作了懶加載),那就是懶漢模式。
 * @author luckyharry
 *
 */
public class MainTest {

    public static void main(String[] args) {
        
        //獲得單例中的執行個體
        SingletonInnerClass.getInstance();
        
        SingletonLazyNotSafe.getInstance();
        
        SingletonLazySafe.getInstance();
        
        SingletonHunger.getInstance();
        
    }
}

           

後記

我會一直以這樣通俗易懂的自己能想到的例子,講解設計模式,最大的啟發是來源于《大話設計模式》。非常感覺這本書的作者程傑先生。