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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java学习-----单例模式

發(fā)布時(shí)間:2025/7/25 java 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java学习-----单例模式 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一.問(wèn)題引入

  偶然想想到的如果把Java的構(gòu)造方法弄成private,那里面的成員屬性是不是只有通過(guò)static來(lái)訪問(wèn)呢;如果構(gòu)造方法是private的話,那么有什么好處呢;如果構(gòu)造方法是private的話,會(huì)不更好的封裝該內(nèi)呢?我主要是應(yīng)用在使用普通類模擬枚舉類型里,后來(lái)發(fā)現(xiàn)這就是傳說(shuō)中的單例模式。構(gòu)造函數(shù)弄成private 就是單例模式,即不想讓別人用new 方法來(lái)創(chuàng)建多個(gè)對(duì)象,可以在類里面先生成一個(gè)對(duì)象,然后寫一個(gè)public static方法把這個(gè)對(duì)象return出去。(eg:public 類名 getInstancd(){return 你剛剛生成的那個(gè)類對(duì)象;}),用static是因?yàn)槟愕臉?gòu)造函數(shù)是私有的,不能產(chǎn)生對(duì)象,所以只能用類名調(diào)用,所有只能是靜態(tài)函數(shù)。成員變量也可以寫getter/setter供外界訪問(wèn)的。如果誰(shuí)要用這個(gè)類的實(shí)例就用有興趣的讀者參看我的這一篇博文http://www.cnblogs.com/hxsyl/archive/2013/03/18/2966360.html。

  第一個(gè)代碼不是單例模式,也就是說(shuō)不一定只要構(gòu)造方法是private的就是單例模式。

?

class A(){private A(){}public name;pulbic static A creatInstance(){return new A();}}A a = A.createInstance(); a.name; //name 屬性

?

public class single{ private static final single s=new single(); private single(){ } public static single getInstance(){ return s; } }

?

二.單例模式概念及特點(diǎn)

  java中單例模式是一種常見(jiàn)的設(shè)計(jì)模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
  單例模式有一下特點(diǎn):
  1、單例類只能有一個(gè)實(shí)例。
  2、單例類必須自己自己創(chuàng)建自己的唯一實(shí)例。
  3、單例類必須給所有其他對(duì)象提供這一實(shí)例。

  單例模式確保某個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。在計(jì)算機(jī)系統(tǒng)中,線程池、緩存、日志對(duì)象、對(duì)話框、打印機(jī)、顯卡的驅(qū)動(dòng)程序?qū)ο蟪1辉O(shè)計(jì)成單例。這些應(yīng)用都或多或少具有資源管理器的功能。每臺(tái)計(jì)算機(jī)可以有若干個(gè)打印機(jī),但只能有一個(gè)Printer Spooler,以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)中。每臺(tái)計(jì)算機(jī)可以有若干通信端口,系統(tǒng)應(yīng)當(dāng)集中管理這些通信端口,以避免一個(gè)通信端口同時(shí)被兩個(gè)請(qǐng)求同時(shí)調(diào)用??傊?#xff0c;選擇單例模式就是為了避免不一致?tīng)顟B(tài),避免政出多頭。

  正是由于這個(gè)特 點(diǎn),單例對(duì)象通常作為程序中的存放配置信息的載體,因?yàn)樗鼙WC其他對(duì)象讀到一致的信息。例如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息可能存放在數(shù)據(jù)庫(kù)或 文件中,這些配置數(shù)據(jù)由某個(gè)單例對(duì)象統(tǒng)一讀取,服務(wù)進(jìn)程中的其他對(duì)象如果要獲取這些配置信息,只需訪問(wèn)該單例對(duì)象即可。這種方式極大地簡(jiǎn)化了在復(fù)雜環(huán)境 下,尤其是多線程環(huán)境下的配置管理,但是隨著應(yīng)用場(chǎng)景的不同,也可能帶來(lái)一些同步問(wèn)題。

?

三.典型例題

  首先看一個(gè)經(jīng)典的單例實(shí)現(xiàn)。

?

public class Singleton {private static Singleton uniqueInstance = null;private Singleton() {// Exists only to defeat instantiation. }public static Singleton getInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}// Other methods... }

?

 Singleton通過(guò)將構(gòu)造方法限定為private避免了類在外部被實(shí)例化,在同一個(gè)虛擬機(jī)范圍內(nèi),Singleton的唯一實(shí)例只能通過(guò)getInstance()方法訪問(wèn)。(事實(shí)上,通過(guò)Java反射機(jī)制是能夠?qū)嵗瘶?gòu)造方法為private的類的,那基本上會(huì)使所有的Java單例實(shí)現(xiàn)失效。此問(wèn)題在此處不做討論,姑且掩耳盜鈴地認(rèn)為反射機(jī)制不存在。)

  但是以上實(shí)現(xiàn)沒(méi)有考慮線程安全問(wèn)題。所謂線程安全是指:如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的?;蛘哒f(shuō):一個(gè)類或者程序所提供的接口對(duì)于線程來(lái)說(shuō)是原子操作或者多個(gè)線程之間的切換不會(huì)導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,也就是說(shuō)我們不用考慮同步的問(wèn)題。顯然以上實(shí)現(xiàn)并不滿足線程安全的要求,在并發(fā)環(huán)境下很可能出現(xiàn)多個(gè)Singleton實(shí)例。

?

public class TestStream {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;} //該類只能有一個(gè)實(shí)例private TestStream(){} //私有無(wú)參構(gòu)造方法//該類必須自行創(chuàng)建//有2種方式/*private static final TestStream ts=new TestStream();*/private static TestStream ts1=null;//這個(gè)類必須自動(dòng)向整個(gè)系統(tǒng)提供這個(gè)實(shí)例對(duì)象public static TestStream getTest(){if(ts1==null){ts1=new TestStream();}return ts1;}public void getInfo(){System.out.println("output message "+name);}}

?

public class TestMain {public static void main(String [] args){TestStream s=TestStream.getTest();s.setName("張孝祥");System.out.println(s.getName());TestStream s1=TestStream.getTest();s1.setName("張孝祥");System.out.println(s1.getName());s.getInfo();s1.getInfo();if(s==s1){System.out.println("創(chuàng)建的是同一個(gè)實(shí)例");}else if(s!=s1){System.out.println("創(chuàng)建的不是同一個(gè)實(shí)例");}else{System.out.println("application error");}}}

?

運(yùn)行結(jié)果:
  張孝祥
  張孝祥
  output message 張孝祥
  output message 張孝祥
  創(chuàng)建的是同一個(gè)實(shí)例
?
結(jié)論:由結(jié)果可以得知單例模式為一個(gè)面向?qū)ο蟮膽?yīng)用程序提供了對(duì)象惟一的訪問(wèn)點(diǎn),不管它實(shí)現(xiàn)何種功能,整個(gè)應(yīng)用程序都會(huì)同享一個(gè)實(shí)例對(duì)象。

  其次,下面是單例的三種實(shí)現(xiàn)?!   ?/p>

    1.餓漢式單例類

  飛哥下面這個(gè)可以不加final,因?yàn)殪o態(tài)方法只在編譯期間執(zhí)行一次初始化,也就是只會(huì)有一個(gè)對(duì)象。

?

//餓漢式單例類.在類初始化時(shí),已經(jīng)自行實(shí)例化 public class Singleton1 {//私有的默認(rèn)構(gòu)造子private Singleton1() {}//已經(jīng)自行實(shí)例化 private static final Singleton1 single = new Singleton1();//靜態(tài)工廠方法 public static Singleton1 getInstance() {return single;}}

?

2.懶漢式單例類

  那個(gè)if判斷確保對(duì)象只創(chuàng)建一次。

?

//懶漢式單例類.在第一次調(diào)用的時(shí)候?qū)嵗?public class Singleton2 {//私有的默認(rèn)構(gòu)造子private Singleton2() {}//注意,這里沒(méi)有final private static Singleton2 single=null;//靜態(tài)工廠方法 public synchronized static Singleton2 getInstance() {if (single == null) { single = new Singleton2();} return single;}}

?

3.登記式單例類

?

import java.util.HashMap;import java.util.Map;//登記式單例類.//類似Spring里面的方法,將類名注冊(cè),下次從里面直接獲取。public class Singleton3 {private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();static{Singleton3 single = new Singleton3();map.put(single.getClass().getName(), single);}//保護(hù)的默認(rèn)構(gòu)造子protected Singleton3(){}//靜態(tài)工廠方法,返還此類惟一的實(shí)例public static Singleton3 getInstance(String name) {if(name == null) {name = Singleton3.class.getName();System.out.println("name == null"+"--->name="+name);}if(map.get(name) == null) {try {map.put(name, (Singleton3) Class.forName(name).newInstance());} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}return map.get(name);}//一個(gè)示意性的商業(yè)方法public String about() { return "Hello, I am RegSingleton."; } public static void main(String[] args) {Singleton3 single3 = Singleton3.getInstance(null);System.out.println(single3.about());}}

?

四.單例對(duì)象作配置信息管理時(shí)可能會(huì)帶來(lái)的幾個(gè)同步問(wèn)題
  

  1.在多線程環(huán)境下,單例對(duì)象的同步問(wèn)題主要體現(xiàn)在兩個(gè)方面,單例對(duì)象的初始化和單例對(duì)象的屬性更新。

    本文描述的方法有如下假設(shè):

    a. 單例對(duì)象的屬性(或成員變量)的獲取,是通過(guò)單例對(duì)象的初始化實(shí)現(xiàn)的。也就是說(shuō),在單例對(duì)象初始化時(shí),會(huì)從文件或數(shù)據(jù)庫(kù)中讀取最新的配置信息。

    b. 其他對(duì)象不能直接改變單例對(duì)象的屬性,單例對(duì)象屬性的變化來(lái)源于配置文件或配置數(shù)據(jù)庫(kù)數(shù)據(jù)的變化。

    1.1單例對(duì)象的初始化

      首先,討論一下單例對(duì)象的初始化同步。單例模式的通常處理方式是,在對(duì)象中有一個(gè)靜態(tài)成員變量,其類型就是單例類型本身;如果該變量為null,則創(chuàng)建該單例類型的對(duì)象,并將該變量指向這個(gè)對(duì)象;如果該變量不為null,則直接使用該變量?!  ?/p>

      這種處理方式在單線程的模式下可以很好的運(yùn)行;但是在多線程模式下,可能產(chǎn)生問(wèn)題。如果第一個(gè)線程發(fā)現(xiàn)成員變量為null,準(zhǔn)備創(chuàng)建對(duì)象;這是第二 個(gè)線程同時(shí)也發(fā)現(xiàn)成員變量為null,也會(huì)創(chuàng)建新對(duì)象。這就會(huì)造成在一個(gè)JVM中有多個(gè)單例類型的實(shí)例。如果這個(gè)單例類型的成員變量在運(yùn)行過(guò)程中變化,會(huì) 造成多個(gè)單例類型實(shí)例的不一致,產(chǎn)生一些很奇怪的現(xiàn)象。例如,某服務(wù)進(jìn)程通過(guò)檢查單例對(duì)象的某個(gè)屬性來(lái)停止多個(gè)線程服務(wù),如果存在多個(gè)單例對(duì)象的實(shí)例,就 會(huì)造成部分線程服務(wù)停止,部分線程服務(wù)不能停止的情況。

    1.2單例對(duì)象的屬性更新

      通常,為了實(shí)現(xiàn)配置信息的實(shí)時(shí)更新,會(huì)有一個(gè)線程不停檢測(cè)配置文件或配置數(shù)據(jù)庫(kù)的內(nèi)容,一旦發(fā)現(xiàn)變化,就更新到單例對(duì)象的屬性中。在更新這些信 息的時(shí)候,很可能還會(huì)有其他線程正在讀取這些信息,造成意想不到的后果。還是以通過(guò)單例對(duì)象屬性停止線程服務(wù)為例,如果更新屬性時(shí)讀寫不同步,可能訪問(wèn)該 屬性時(shí)這個(gè)屬性正好為空(null),程序就會(huì)拋出異常。

      下面是解決方法

?

//單例對(duì)象的初始化同步 public class GlobalConfig {private static GlobalConfig instance = null;private Vector properties = null;private GlobalConfig() {//Load configuration information from DB or file//Set values for properties }private static synchronized void syncInit() {if (instance == null) {instance = new GlobalConfig();}}public static GlobalConfig getInstance() {if (instance == null) {syncInit();}return instance;}public Vector getProperties() {return properties;}}

?

這種處理方式雖然引入了同步代碼,但是因?yàn)檫@段同步代碼只會(huì)在最開(kāi)始的時(shí)候執(zhí)行一次或多次,所以對(duì)整個(gè)系統(tǒng)的性能不會(huì)有影響。

  單例對(duì)象的屬性更新同步。

  參照讀者/寫者的處理方式,設(shè)置一個(gè)讀計(jì)數(shù)器,每次讀取配置信息前,將計(jì)數(shù)器加1,讀完后將計(jì)數(shù)器減1.只有在讀計(jì)數(shù)器為0時(shí),才能更新數(shù)據(jù),同時(shí)要阻塞所有讀屬性的調(diào)用。

  代碼如下:

?

public class GlobalConfig {private static GlobalConfig instance;private Vector properties = null;private boolean isUpdating = false;private int readCount = 0;private GlobalConfig() {//Load configuration information from DB or file//Set values for properties }private static synchronized void syncInit() {if (instance == null) {instance = new GlobalConfig();}}public static GlobalConfig getInstance() {if (instance==null) {syncInit();}return instance;}public synchronized void update(String p_data) {syncUpdateIn();//Update properties }private synchronized void syncUpdateIn() {while (readCount > 0) {try {wait();} catch (Exception e) {}}}private synchronized void syncReadIn() {readCount++;}private synchronized void syncReadOut() {readCount--;notifyAll();}public Vector getProperties() {syncReadIn();//Process data syncReadOut();return properties;}}

?

采用"影子實(shí)例"的辦法具體說(shuō),就是在更新屬性時(shí),直接生成另一個(gè)單例對(duì)象實(shí)例,這個(gè)新生成的單例對(duì)象實(shí)例將從數(shù)據(jù)庫(kù)或文件中讀取最新的配置信息;然后將這些配置信息直接賦值給舊單例對(duì)象的屬性。

?

public class GlobalConfig {private static GlobalConfig instance = null;private Vector properties = null;private GlobalConfig() {//Load configuration information from DB or file//Set values for properties }private static synchronized void syncInit() {if (instance = null) {instance = new GlobalConfig();}}public static GlobalConfig getInstance() {if (instance = null) {syncInit();}return instance;}public Vector getProperties() {return properties;}public void updateProperties() {//Load updated configuration information by new a GlobalConfig objectGlobalConfig shadow = new GlobalConfig();properties = shadow.getProperties();}}

?

注意:在更新方法中,通過(guò)生成新的GlobalConfig的實(shí)例,從文件或數(shù)據(jù)庫(kù)中得到最新配置信息,并存放到properties屬性中。上面兩個(gè)方法比較起來(lái),第二個(gè)方法更好,首先,編程更簡(jiǎn)單;其次,沒(méi)有那么多的同步操作,對(duì)性能的影響也不大。

?

?五.結(jié)束語(yǔ)

  參考了很多資料才整理出了該內(nèi)容,請(qǐng)大家多多指教,期間受益頗多,感覺(jué)到OS很重要,等把網(wǎng)絡(luò)版的俄羅斯方塊搞好后就去復(fù)習(xí)OS……加油,我是最棒的!!!

?  參考文獻(xiàn):http://www.cnblogs.com/whgw/archive/2011/10/05/2199535.html,http://blog.csdn.net/leesidong/article/details/6024455

轉(zhuǎn)載于:https://www.cnblogs.com/dragon1013/p/5036027.html

總結(jié)

以上是生活随笔為你收集整理的Java学习-----单例模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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