實現方式
共三種:
- 餓漢式
- 懶漢式
- 枚舉單例
- 靜態內部類式 (推薦)
餓漢式
原理:類在初始化加載的時候,會加載內部 靜態 成員對象,然後方法被調用的時候,直接返回這個對象
/**
* 測試餓漢式單例模式
* @author 尚學堂高淇 www.sxt.cn
*
*/
public class Demo {
private Demo(){}; //私有化構造器
private static Demo demo = new Demo(); //類初始化時,立即加載這個對象(沒有延時加載的優勢)。加載類時,天然的是線程安全的!
public static Demo getInstance(){ //方法沒有同步,調用效率高!
return demo;
}
}
懶漢式
原理:方法被外部調用的時候,先判斷對象是否被實例化,如果沒有,則實例化返回,如果有 直接返回 該方法需要加同步鎖synchronized,
/**
* 測試懶漢式單例模式
*/
public class Demo {
private Demo(){};
private static Demo demo;
public static synchronized Demo getInstance(){ //方法同步,調用效率低!
if(demo == null){
demo = new Demo();
}
return demo;
}
}
枚舉單例
優點:
1.實現簡單
2.枚舉本身就是單例模式。由JVM從根本上提供保障 避免通過反射和反序列化的漏洞
缺點:
1.無延遲加載
/**
* 測試枚舉式實現單例模式(沒有延時加載)
*/
public enum SingletonDemo5 {
//這個枚舉元素,本身就是單例對象!
INSTANCE;
//添加自己需要的操作!
public void singletonOperation(){
}
}
靜態內部類式 推薦
1.沒有靜態 內部 成員對象 所以和餓漢式不一樣
2.靜態內部類,在調用的時候初始化 懶
3.內部類中成員對象是 static final 保證了在內存中只有一份實例,
/**
* 測試靜態內部類實現單例模式
* 這種方式:線程安全,調用效率高,並且實現了延時加載!
* @author 尚學堂高淇 www.sxt.cn
*
*/
public class Demo {
private Demo(){};
//靜態內部類
private static class DemoInstance {
private static final Demo instance = new Demo();
}
public static Demo getInstance(){
return DemoInstance.instance;
}
}
比較
主要
- 餓漢式(線程安全,調用效率高,但是 不能延時加載)
- 懶漢式 (線程安全,調用效率不高,但是 可以延時加載) 每次調用getInstance() 方法都要同步,併發效率太低
- 靜態內部類式(線程安全,調用效率高。可以延時加載)
- 枚舉單例(線程安全,調用效率高,不能延時加載)
測試效率
/**
* 測試多線程環境下五種創建單例模式的效率
*/
public class Client3 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
int threadNum = 10;
final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
for(int i=0;i<threadNum;i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<1000000;i++){
// Object o = SingletonDemo4.getInstance();
Object o = SingletonDemo5.INSTANCE;
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await(); //main線程阻塞,直到計數器變爲0,纔會繼續往下執行!
long end = System.currentTimeMillis();
System.out.println("總耗時:"+(end-start));
}
}