單例模式是一種常見的設計模式,並且有幾種不同的寫法,不同的寫法有不同的效果。
1.懶漢式
public class SingletonThread implements Runnable{
@Override
public void run() {
try {
System.out.println(Singleton.getInstance());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
這種寫法是最簡單的形式,在第一次調用getInstance方法的時候才實例化,缺點是線程不安全。
測試:
修改Singleton:
public class Singleton {
private Singleton() {
};
private static Singleton singleton = null;
public static Singleton getInstance() throws InterruptedException {
if(singleton == null) {
Thread.sleep(100);// 延遲實例化時間,加強測試效果。
singleton = new Singleton();
}
return singleton;
}
}
SingletonThread測試類:
public class SingletonThread implements Runnable{
@Override
public void run() {
try {
System.out.println(Singleton.getInstance()); // 打印得到的Singleton實例hashcode
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Main測試類如下,使用多個線程調用getInstance()方法,
public class Main {
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
SingletonThread s = new SingletonThread();
Thread t = new Thread(s);
t.start();
}
}
}
運行之後:
com.sj.pattern.Singleton@17823918
com.sj.pattern.Singleton@401e9c3f
com.sj.pattern.Singleton@6150818a
com.sj.pattern.Singleton@610f7612
com.sj.pattern.Singleton@3e10c986
com.sj.pattern.Singleton@4ecac02f
com.sj.pattern.Singleton@4da9ec16
com.sj.pattern.Singleton@5faecf45
com.sj.pattern.Singleton@667262b6
com.sj.pattern.Singleton@19a40cfc
可以看到,生成了10個不同的Singleton實例,懶漢式寫法在多線程環境下可能不會產生真正的單例對象。這樣的問題可以再getInstance()方法之前加synchronized來使線程同步,但是這樣的寫法效率低,並且多線程環境下很少需要同步。
2.餓漢式
public class Singleton {
private Singleton() {
};
private static Singleton singleton = new Singleton();
public static synchronized Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
餓漢式是在加載類的時候就生成一個實例,避免了線程不安全。public class Singleton {
private Singleton() {};
private static Singleton singleton = null;
static {
singleton = new Singleton();
}
public static synchronized Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
變種寫法的特點在於singleton實例化將在static代碼塊中進行,與上述的區別僅僅在於生成實例的時機不同。並且這兩種方式都沒有實現延遲加載。
3.靜態內部類
public class Singleton {
private Singleton() {};
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static synchronized Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
靜態內部類方式採用了ClassLoader的機制保證了INSTANCE實例的單例化,並且可以實現延遲加載,即在真正需要使用INSTANCE的時候才實例化。4.雙重校驗鎖
public class Singleton {
private volatile static Singleton singleton = null;
public static Singleton getInstance() {
if(singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
雙重校驗鎖方式是懶漢式的升級版,在多線程環境下可以保證實例的唯一性。