单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析
生活随笔
收集整理的這篇文章主要介紹了
单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
剛剛線程1看不到線程0的重排序,我們創(chuàng)建一個(gè)類,這個(gè)方案是使用靜態(tài)內(nèi)部類來解決,一會(huì)我們也會(huì)分析一下原理,我們創(chuàng)建一個(gè)靜態(tài)內(nèi)部類,靜態(tài)內(nèi)部類的代理模式,JVM在類的初始化階段,也就是class被加載后,并且被線程使用之前,都是類的初始化階段,在這個(gè)階段會(huì)執(zhí)行類的初始化,那在執(zhí)行類的初始化期間呢,JVM會(huì)去獲取一個(gè)鎖,這個(gè)鎖可以同步多個(gè)線程,對(duì)一個(gè)類的初始化,也就是綠色的部分,基于這個(gè)特性,我們可以實(shí)現(xiàn)基于內(nèi)部類的并且是線程安全的,延遲初始化方案,那我們看一下這個(gè)圖,還是線程0和線程1,藍(lán)色和紅色,那在這種實(shí)現(xiàn)模式中呢,對(duì)于右側(cè)的2和3,也就是橙色的框,這兩個(gè)步驟的重排序,對(duì)于我們前面講的,線程1并不會(huì)看到,也就是說,非構(gòu)造線程是不允許看到重排序的,因?yàn)槲覀冎笆侵v的線程0來構(gòu)造這個(gè)單例對(duì)象,初始化一個(gè)類,包括執(zhí)行類的靜態(tài)初始化,還有初始化在這個(gè)類中聲明的靜態(tài)變量,根據(jù)JAVA語言規(guī)范,主要分5種情況,首次發(fā)生的時(shí)候呢,一個(gè)類將被立刻初始化,這里所說的類呢,泛指包括接口interface,也是一個(gè)類,那假設(shè)這個(gè)類是A,那現(xiàn)在我們說一下,這幾種情況,都會(huì)導(dǎo)致這個(gè)A類,被立刻初始化,首先呢第一種情況,有一個(gè)A類型的實(shí)例被創(chuàng)建,A類型中的一個(gè)靜態(tài)方法被調(diào)用,第三種情況呢,是A類中聲明的一個(gè)靜態(tài)成員,被賦值,第四種情況,A類中聲明的一個(gè)靜態(tài)成員被使用,并且這個(gè)成員不是一個(gè)常量成員,前四種我們實(shí)際工作中用的比較多,第五種用的比較少,也就是說如果A類是一個(gè)頂級(jí)類,關(guān)于頂級(jí)類在JAVA語言規(guī)范里面的介紹,并且呢在這個(gè)類中,有嵌套的斷言語句,這種情況呢A類也會(huì)立刻被初始化,也就是說剛剛說的五種情況,前四種是我們經(jīng)常會(huì)被碰到的,只要首次發(fā)生以上說的一個(gè)情況,這個(gè)類就會(huì)被立刻初始化,把我們看一下這個(gè)圖,當(dāng)線程0和線程1試圖想獲取這個(gè)鎖的時(shí)候,也就是獲得class對(duì)象的初始化鎖,這個(gè)時(shí)候肯定只有一個(gè)線程能獲得這個(gè)鎖,假設(shè)線程0獲得這個(gè)鎖了,線程0執(zhí)行內(nèi)部類的一個(gè)初始化,對(duì)于靜態(tài)內(nèi)部類即使23之間存在重排序,但是線程1是無法看到這個(gè)重排序的,因?yàn)檫@里有一個(gè)class對(duì)象的初始化鎖,因?yàn)檫@里面有鎖,對(duì)于線程0而言,初始化這個(gè)靜態(tài)內(nèi)部類的時(shí)候,也就是把這個(gè)instance new出來,可以看到我們這里還有一個(gè)大框,所以23怎么排序無所謂,線程1看不到,因?yàn)榫€程1在綠色區(qū)域等待著,所以靜態(tài)內(nèi)部類就是基于類初始化的延遲加載解決方案,那我們回到代碼里
package com.learn.design.pattern.creational.singleton;/*** 前面說了我們使用靜態(tài)內(nèi)部類* * 所以靜態(tài)內(nèi)部類核心在于* InnerClass類的初始化鎖* 看哪個(gè)線程拿到* 哪個(gè)線程就去初始化他* 對(duì)于這種情況呢* 我們來測(cè)試一下* 打開我們的線程類* 還是用多線程來測(cè)試* * 非常重要的一點(diǎn)* 我們沒有寫私有的構(gòu)造函數(shù)* * * @author Leon.Sun**/
public class StaticInnerClassSingleton {/*** 這個(gè)class要聲明成private的class* 權(quán)限一定要控制好* * * @author Leon.Sun**/private static class InnerClass{/*** 那這里聲明一個(gè)什么呢* 這個(gè)名字還是用類名來做* new一個(gè)StaticInnerClassSingleton* 那在這個(gè)靜態(tài)內(nèi)部類里邊直接new了一個(gè)StaticInnerClassSingleton這個(gè)類的對(duì)象* 并且它是private和static的* * */private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();}/*** 那我們要開放獲取這個(gè)對(duì)象的方法* 所以是public的* 返回值肯定是StaticInnerClassSingleton* return什么呢* 通過這個(gè)靜態(tài)內(nèi)部類InnerClass點(diǎn)靜態(tài)的成員staticInnerClassSingleton* 那這個(gè)靜態(tài)內(nèi)部類的單例模式就完成了* 非常簡(jiǎn)單* 那我們講一下原理* 我們看一下圖* * * @return*/public static StaticInnerClassSingleton getInstance(){return InnerClass.staticInnerClassSingleton;}/*** 私有的構(gòu)造器肯定是要有的* 否則外部就可以new出來了* 所以這一點(diǎn)千萬不要忘記* 如果之前寫的時(shí)候沒有發(fā)現(xiàn)這個(gè)問題的話* 對(duì)聲明構(gòu)造器一定要加深* 現(xiàn)在我們來到Test直接run一下* 我們可以看到線程0和線程1拿到的是同一個(gè)* 因?yàn)檫@個(gè)方式非常簡(jiǎn)單* 就不debug了* 靜態(tài)內(nèi)部類和簽名的doublecheck* 都是為了做延遲初始化* 來降低創(chuàng)建單例實(shí)例的開銷* 所以具體在業(yè)務(wù)場(chǎng)景中采用什么方案呢* 還要看我們的單例對(duì)象* 是什么樣的* 另外一點(diǎn)* 如果問設(shè)計(jì)模式* 單例模式會(huì)被百分之九十九問到* 那么單例模式剛剛講的這一種方案* 一定要理解透* 這個(gè)是一個(gè)循序漸進(jìn)的過程* 而且單例模式非常重要* 如果可以一層一層迭代* 希望對(duì)著兩種方案和思路來引入靜態(tài)內(nèi)部類和doublecheck一步一步解決什么問題* 可以自己回顧一下* 總結(jié)一下* * * */private StaticInnerClassSingleton(){if(InnerClass.staticInnerClassSingleton != null){throw new RuntimeException("單例構(gòu)造器禁止反射調(diào)用");}}
}
package com.learn.design.pattern.creational.singleton;public class T implements Runnable {@Overridepublic void run() {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// System.out.println(Thread.currentThread().getName()+" "+lazySingleton);
// LazyDoubleCheckSingleton instance = LazyDoubleCheckSingleton.getInstance();/*** 通過這個(gè)類的getInstance方法* 看上去沒有什么區(qū)別* 但是里面使用的是靜態(tài)內(nèi)部類* 而且是一個(gè)private的* */StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();;// ContainerSingleton.putInstance("object",new Object());
// Object instance = ContainerSingleton.getInstance("object");
// ThreadLocalInstance instance = ThreadLocalInstance.getInstance();System.out.println(Thread.currentThread().getName()+" "+instance);}
}
package com.learn.design.pattern.creational.singleton;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());Thread t1 = new Thread(new T());Thread t2 = new Thread(new T());t1.start();t2.start();System.out.println("program end");// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());
//
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);
//
// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
//HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();
//
// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;// Class objectClass = LazySingleton.class;
// Class objectClass = EnumInstance.class;// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
// constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();// System.out.println(instance);
// System.out.println(newInstance);
// System.out.println(instance == newInstance);// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();}
}
?
總結(jié)
以上是生活随笔為你收集整理的单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DoubleCheck双重检查实战及原理
- 下一篇: 单例设计模式-饿汉式