利用static來實現單例模式

靈劍山真人發表於2022-01-08

一:之前舊的寫法

class Singleton{
    private Singleton() {}
    private static Singleton instance = null;
    public synchronized static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
}

就利用Sington.getInstace就可以了,獲得的是同一個例項。上面那個程式碼有兩個優點:

  1. 懶載入,把在堆建立例項這個行為延遲到類的使用時。
  2. 鎖效果,防止生成多個例項,因為synchronized修飾這個static方法,所以相當於給這個方法加上了一個類鎖?。

 

二:static程式碼塊的效果

先來看一段程式碼:

class StaticClass{
    private static int a = 1;
    static{
        System.out.println("語句1");
        System.out.println("語句2");
    }
    static{
        System.out.println("語句3");
        System.out.println("語句4");
    }
}

當在多個執行緒同時觸發類的初始化過程的時候(在初始化過程,類的static變數會被賦值為JVM預設值並且static程式碼塊會被執行),為什麼static不會被多次執行?因為有可能兩個執行緒同時檢測到類還沒被初始化,然後都執行static程式碼塊,結果就把語句1234多列印了,為什麼上述情況不會發生。

Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Class.forName("StaticClass");//這一行觸發類的初始化導致靜態程式碼塊執行
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Class.forName("StaticClass");//同樣
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
        thread2.start();

結果如下:

語句1
語句2
語句3
語句4

有一段英文對其進行了解釋:

static initialization block can be triggered from multiple parallel threads (when the loading of the class happens in the first time), Java runtime guarantees that it will be executed only once and in thread-safe manner + when we have more than 1 static block - it guarantees the sequential execution of the blocks, 也就是說,java runtime幫我們做了兩件事:

  1. 並行執行緒中,都出現了第一次初始化類的情況,保證類的初始化只執行一次。
  2. 保證static程式碼塊的順序執行

 

三:單例的另一種寫法

有了對static的知識的瞭解之後,我們可以寫出這樣的單例模式:

class Singleton{
    private Singleton() {}
    private static class NestedClass{
       static Singleton instance = new Singleton();//這條賦值語句會在初始化時才執行
    }
    public static Singleton getInstance() {
        return NestedClass.instance;
    }
}
  1. 懶載入,因為static語句會在初始化時才賦值執行,達到了懶載入的效果。
  2. 鎖?的效果由Java runtime保證,虛擬機器幫我們保證static語句在初始化時只會執行一次。

 

四:總結

如果不知道static的基礎知識和虛擬機器類載入的知識,我可能並不會知道這一種方法。理論永遠先行於技術,要學好理論才能從根本上提升自己。

 

本博文站在以下這位巨人的肩膀上:https://www.linkedin.com/pulse/static-variables-methods-java-where-jvm-stores-them-kotlin-malisciuc

 

相關文章