日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

GOF23设计模式(创建型模式)单例模式

發(fā)布時間:2023/11/28 生活经验 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GOF23设计模式(创建型模式)单例模式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄:

一:單例模式的核心作用、常見應用場景

二:五種單例模式及其實現(xiàn)

三:關于反射和反序列化破解單例模式的漏洞,以及相應的解決方案

四:測試五種單例模式的效率


一:核心作用及常見應用場景:

核心作用:保證一個類只有一個實例對象,并且提供一個訪問該實例對象的全局訪問點(獲取實例對象的方法),通過該訪問點獲得對象唯一

常見應用場景:

1

Windows的 Task Manager(任務管理器)就是很典型的單例模式(無論打開多少次任務管理器,只會出現(xiàn)一個任務管理器)

2

windows的 Recycle Bin(回收站)也是典型的單例應用。在整個系統(tǒng)運行過程中,回收站直維護著僅有的一個實例

3

項目中,讀取配置文件的類,一般也只有一個對象。沒有必要每次使用配置文件數(shù)據(jù),每次new一個對象去讀取。

4

網(wǎng)站的計數(shù)器,一般也是采用單例模式實現(xiàn),否則難以同步。

5

應用程序的日志應用,一般都何用單例模式實現(xiàn),這一般是由于共享的日志文件一直處于打開狀態(tài),因為只能有一個實例去操作,否則內(nèi)容不好追加。

6

數(shù)據(jù)庫連接池的設計一般也是采用單例模式,因為數(shù)據(jù)庫連接是一種數(shù)據(jù)庫資源

7

操作系統(tǒng)的文件系統(tǒng),也是大的單例模式實現(xiàn)的具體例子,一個操作系統(tǒng)只能有一個文件系統(tǒng)。

8

Application也是單例的典型應用( Servlet編程中會涉及到)

9

在 Spring中,每個Bean默認就是單例的,這樣做的優(yōu)點是 Spring容器可以管理

10

在 servlet編程中,每個Sere也是單例

11

在 spring MVC框架/ struts1框架中,控制器對象也是單例


二:五種單例模式及其實現(xiàn)方式~~(附代碼)

主要

餓漢式(線程安全,調(diào)用效率高。但是,不能延時加載。)

懶漢式(線程安全,調(diào)用效率不高。但是,可以延時加載。)

其他

雙重檢測鎖式(由于JVM底層內(nèi)部模型原因,偶爾會出問題。不建議使用

靜態(tài)內(nèi)部類式(線程安全,調(diào)用效率高。但是,可以延時加載)

枚舉單例(線程安全,調(diào)用效率高,不能延時加載)

1. 餓漢式:

餓漢式單例模式代碼中, static變量會在類裝載時初始化,此時也不會涉及多個線程對象訪問該對象的問題。虛擬機保證只會裝載一次該類,肯定不會發(fā)生并發(fā)訪問的問題。因此,可以省略synchronized關鍵字。

問題:如果只是加載本類,而不是要調(diào)用 getinstance(),甚至永遠沒有調(diào)用,則會造成資源浪費!

package 設計模式;/*** 測試餓漢式單例模式* 線程安全* 立即加載*/
public class Singleton1 {//類初始化時立即加載(沒有延時加載的優(yōu)勢),加載類時,天然是線程安全的public static Singleton1 instance = new Singleton1();//構(gòu)造器私有private Singleton1() {}//方法沒有同步,調(diào)用效率高public static Singleton1 getInstance() {return instance;}public static void main(String[] args) {Singleton1 singleton1 = Singleton1.getInstance();Singleton1 singleton2 = singleton1.getInstance();System.out.println(singleton1 == singleton2);//true}
}

2.? 懶漢式

要點: lazy load 延遲加載,只有真正用的時候才加載!

問題: 資源利用率高了。但是,每次調(diào)用 getinstanceO方法都要同步,并發(fā)效率較低。

package 設計模式;/*** 測試懶漢式單例模式* 線程安全* 延時加載*/
public class Singleton2 {//類初始化時,不初始化這個對象(延時加載,真正用的時候才加載)public static Singleton2 instance;//構(gòu)造器私有private Singleton2() {}//方法同步,調(diào)用效率低public static Singleton2 getInstance() {if (instance == null)instance = new Singleton2();return instance;}public static void main(String[] args) {Singleton2 singleton1 = Singleton2.getInstance();Singleton2 singleton2 = singleton1.getInstance();System.out.println(singleton1 == singleton2);//true}
}

3.?雙重檢測鎖式:(不建議使用!!)

看看代碼就ok~

package 設計模式;/*** 測試雙重檢查鎖單例模式* 不建議使用*/
public class Singleton3 {//類初始化時,不初始化這個對象(延時加載,真正用的時候才加載)public static Singleton3 instance = null;//構(gòu)造器私有private Singleton3() {}//方法同步,調(diào)用效率低public static Singleton3 getInstance() {if (instance == null) {Singleton3 sc;synchronized (Singleton3.class) {sc = instance;if (sc == null) {synchronized (Singleton3.class) {if (sc == null)sc = new Singleton3();}instance = sc;}}}return instance;}public static void main(String[] args) {Singleton3 singleton1 = Singleton3.getInstance();Singleton3 singleton2 = singleton1.getInstance();System.out.println(singleton1 == singleton2);//true}
}

4.?靜態(tài)內(nèi)部類式:

要點:

  1. 外部類沒有 static屬性,則不會像餓漢式那樣立即加載對象
  2. 只有真正調(diào)用 getinstance(),才會加載靜態(tài)內(nèi)部類。加載類時是線程安全的。 Instance是 static final類型,保證了內(nèi)存中只有這樣一個實例存在,而且只能被賦值一次,從而保證了線程安全性。
  3. 兼?zhèn)淞瞬l(fā)高效調(diào)用和延遲加載的優(yōu)勢!
package 設計模式;/*** 測試靜態(tài)內(nèi)部類單例模式* 線程安全,調(diào)用效率高,并且實現(xiàn)延遲加載*/
public class Singleton4 {//靜態(tài)內(nèi)部類private static class SingletonClassInstance {private static final Singleton4 instance = new Singleton4();}//構(gòu)造器私有private Singleton4() {}public static Singleton4 getInstance() {return SingletonClassInstance.instance;}public static void main(String[] args) {Singleton4 singleton1 = Singleton4.getInstance();Singleton4 singleton2 = singleton1.getInstance();System.out.println(singleton1 == singleton2);//true}
}

5.?枚舉單例:

優(yōu)點: 實現(xiàn)簡單,枚舉本身就是單例模式。由JVM從根本上提供保障!避免通過反射和反序列化的漏洞!

缺點:無延遲加載

package 設計模式;/*** 測試枚舉單例模式* 實現(xiàn)簡單,枚舉本身就是單例模式。由JVM從根本上提供保障!避免通過反射和反序列化的漏洞!* 無延時加載*/
public enum Singleton5 {//這個枚舉元素,本身就是單例模式INSTANCE;//添加自己需要的操作public void singletonOperation() {}public static void main(String[] args) {Singleton5 singleton1 = Singleton5.INSTANCE;Singleton5 singleton2 = singleton1.INSTANCE;System.out.println(singleton1 == singleton2);//true}
}

三:反射和反序列化漏洞及其解決方案


1.反射可以破解上面幾種實現(xiàn)方式~(不包含枚舉式)

示例:通過反射破解靜態(tài)內(nèi)部類的單例模式(聯(lián)系上方靜態(tài)內(nèi)部類實現(xiàn)單例模式的代碼)

package 設計模式;import java.lang.reflect.Constructor;/*** 避免通過反射破解靜態(tài)內(nèi)部類單例模式*/
public class singleProblem1 {public static void main(String[] args) throws Exception {Singleton4 s1 = Singleton4.getInstance();Singleton4 s2 = Singleton4.getInstance();System.out.println(s1);System.out.println(s2);//通過反射直接調(diào)用私有構(gòu)造器Class c = Class.forName("設計模式.Singleton4");System.out.println(c);Constructor constructor = c.getDeclaredConstructor(null);constructor.setAccessible(true);Singleton4 s3 = (Singleton4) constructor.newInstance();Singleton4 s4 = (Singleton4) constructor.newInstance();System.out.println(s3);System.out.println(s4);}
}

根據(jù)結(jié)果,發(fā)現(xiàn)產(chǎn)生了兩個新對象,我們的解決方案就是?在私有構(gòu)造器中手動拋出異常控制,在私有構(gòu)造器內(nèi)加入以下代碼

解決方法:可以在私有構(gòu)造器中手動拋出異常控制

//構(gòu)造器私有private Singleton4() {if (getInstance() != null)throw new RuntimeException();}

?根據(jù)實驗結(jié)果發(fā)現(xiàn),已經(jīng)無法通過反射用私有構(gòu)造器創(chuàng)建新的對象!!

解決成功!!!

?

2.反序列化可以破解上面幾種(不包含枚舉式)實現(xiàn)方式!

示例:通過反序列化破解靜態(tài)內(nèi)部類的單例模式(聯(lián)系上方靜態(tài)內(nèi)部類實現(xiàn)單例模式的代碼)

package 設計模式;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;/*** 避免通過反射破解靜態(tài)內(nèi)部類單例模式*/
public class singleProblem1 {public static void main(String[] args) throws Exception {Singleton4 s1 = Singleton4.getInstance();Singleton4 s2 = Singleton4.getInstance();System.out.println(s1);System.out.println(s2);//通過反序列化的的方式構(gòu)造多個對象//序列化ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:/學習/IDEA project/project/src/設計模式/a.txt"));out.writeObject(s1);out.close();//反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/學習/IDEA project/project/src/設計模式/a.txt"));Singleton4 s3 = (Singleton4) in.readObject();System.out.println(s3);//如果沒有readResolve方法則會構(gòu)造出一個新對象,破解了單例}
}

根據(jù)結(jié)果,發(fā)現(xiàn)產(chǎn)生了一個新對象,我們的解決方案就是在上方靜態(tài)內(nèi)部類實現(xiàn)單例的代碼中加入readResolve函數(shù)

? 解決方法:可以通過定義readResolve()防止獲得不同對象。

??反序列化時,如果對象所在類定義了readResolve(),(實際是一種回調(diào)),定義返回哪個對象

 //為了防止反序列化破解,添加此方法//如果定義了readResolve方法,則直接返回此方法指定的對象,而不需要再單獨創(chuàng)建對象private Object readResolve() {return getInstance();}

根據(jù)新的實驗結(jié)果,我們發(fā)現(xiàn)新創(chuàng)建出來的對象與通過單例模式創(chuàng)建出的對象相同,即解決成功!!


四:測試單例模式的效率?

通過一下代碼可以測試上述五種方法效率,一下為測試Singleton1,即餓漢式實現(xiàn)單例的時間!!!!

package 設計模式;import java.util.concurrent.CountDownLatch;public class testtime {public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();int threadNum = 10;CountDownLatch countDownLatch = new CountDownLatch(threadNum);for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100000; j++)Singleton1.getInstance();countDownLatch.countDown();}}).start();}countDownLatch.await();//main線程阻塞,知道計數(shù)器變?yōu)?,才會繼續(xù)往下執(zhí)行l(wèi)ong end = System.currentTimeMillis();System.out.println("總耗時:" + (end - start));}
}

其中,很重要的一個類

CountDownLatch

同步輔助類,在完成組正在其他線程中執(zhí)行的操作之前,它允許一個或多個線程一直等待

countDown()

當前線程調(diào)此方法,則計數(shù)減1(建議放在 finally里執(zhí)行)

await()

調(diào)用此方法會一直阻塞當前線程,直到計時器的值為0

在此方法中,在主線程執(zhí)行后,啟動了十個線程?,其中通過CountDownLatch類 實例化出來的countdownlatch相當于一個計數(shù)器,初始化為10,每當一個線程執(zhí)行完后減一,一直阻塞著主線程,直到這十個線程都執(zhí)行完畢,則主線程停止阻塞,記錄最后的時間end,則end-start即為執(zhí)行這是個線程所需要的時間!


THE END

總結(jié)

以上是生活随笔為你收集整理的GOF23设计模式(创建型模式)单例模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。