閒來無事看之前的博客,發現單例模式只會寫2中。所以再重新開一篇博客,將目前自己所能理解的幾種單例模式全部總結下。
___________________________________________________________________________________________________________
1、懶漢式(最基本的) 單線程版
寫單例模式(餓漢式)的步驟:
1):必須在該類中,自己先創建出一個對象。
2):私有化自身的構造器,防止外界通過構造器創建新的對象。
3):想外暴露一個公共的靜態方法用於獲取自身的對象
缺點:單線程是沒問題的 但是多線程就會產生線程問題 下面會介紹多線程版本
// 懶漢式類初始化的,不會創建該對象,真正需要的時候纔會加載(創建),天生線程不安全,需要解決線程安全問題,所有效率比較低
public class SingletonLazy {
private SingletonLazy() {
System.out.println(Thread.currentThread().getName());
}
private static SingletonLazy singletonLazy;
public static SingletonLazy getInstance() {
if (singletonLazy == null) {
singletonLazy = new SingletonLazy();
}
return singletonLazy;
}
public static void main(String[] args) {
SingletonLazy singletonLazy1 = SingletonLazy.getInstance();
SingletonLazy singletonLazy2 = SingletonLazy.getInstance();
System.out.println(singletonLazy1 == singletonLazy2); // true
}
}
多線程版本線程不安全(最簡單的案例):
public class SingletonLazy {
private SingletonLazy() {
System.out.println(Thread.currentThread().getName());
}
private static SingletonLazy singletonLazy;
public static SingletonLazy getInstance() {
if (singletonLazy == null) {
singletonLazy = new SingletonLazy();
}
return singletonLazy;
}
public static void main(String[] args) {
for (int i = 0; i <= 500; i++) {
new Thread(() -> {
try {
getInstance();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
2、餓漢式(最基本的)
優點:線程天生安全 類在整個生命週期中只會被加載一次,因此該單例類只會創建一個實例,也就是說,線程每次都只能也必定只可以拿到這個唯一的對象
缺點:類加載的時候就會加載 static 對象 如果暫時用不到呢 就會佔用極大的內存
public class SingletonHungry {
// 會浪費內存
// byte[] data1 = new byte[1024*1024];
// byte[] data2 = new byte[1024*1024];
private SingletonHungry() {
System.out.println(Thread.currentThread().getName());
}
//小知識: 當使用static 修飾時 會存檔在 JVM的方法區 JVM垃圾回收機制 不會進行回收
private static final SingletonHungry singletonDemo = new SingletonHungry();
private static SingletonHungry getInstance() {
return singletonDemo;
}
public static void main(String[] args) {
SingletonHungry singletonDemo = getInstance();
SingletonHungry singletonDemo2 = getInstance();
System.out.println(singletonDemo == singletonDemo2); // true
}
}
3、枚舉
枚舉是一個特殊的類
內部將構造器進行私有化,因此不能通過New 的方式進行創建
有興趣可以測試下 ,我這比較簡單
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
4、靜態內部類
public class SingletonStatic {
private SingletonStatic() {}
public static class SingletonClassInstance{
private static final SingletonStatic single = new SingletonStatic();
}
public static SingletonStatic getInstance(){
return SingletonClassInstance.single;
}
public static void main(String[] args) {
SingletonStatic singleton1 = SingletonStatic.getInstance();
SingletonStatic singleton2 = SingletonStatic.getInstance();
System.out.println(singleton1 ==singleton2);
}
}
5、DCL(雙重檢驗鎖)
在 new MultiSingletonDCL();時候由於會發生指令重排序 可能會出現問題 因此加上關鍵字 volatile
// 1. 分配內存空間
// 2. 執行構造方法,初始化對象
// 3. 把這個對象指向這個空間
// 雙鎖機制的出現是爲了解決前面同步問題和性能問題
public class SingletonDCL {
private volatile static MultiSingletonDCL multiSingletonDCL;
private SingletonDCL() {
System.out.println(Thread.currentThread().getName());
}
private static SingletonDCL getInstance() {
if (singletonLazyDCL == null){
synchronized (SingletonDCL.class){
if (singletonLazyDCL == null){
singletonLazyDCL = new SingletonDCL();
}
}
}
return singletonLazyDCL;
}
public static void main(String[] args) {
SingletonDCL instance = SingletonDCL.getInstance();
SingletonDCL instance2 = SingletonDCL.getInstance();
System.out.println(instance == instance2);
}
}
本文如有問題,希望大佬指正。不勝感激。