国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
單例模式與雙重檢測(cè)

首先要解釋一下什么是延遲加載,延遲加載就是等到真真使用的時(shí)候才去創(chuàng)建實(shí)例,不用時(shí)不要去創(chuàng)建。

從速度和反應(yīng)時(shí)間角度來(lái)講,非延遲加載(又稱餓漢式)好;從資源利用效率上說(shuō),延遲加載(又稱懶漢式)好

下面看看幾種常見(jiàn)的單例的設(shè)計(jì)方式:

第一種:非延遲加載單例類

Java代碼
  1. public class Singleton {   
  2.  private Singleton() {}   
  3.  private static final Singleton instance = new Singleton();   
  4.  public static Singleton getInstance() {   
  5.   return instance;   
  6.  }   
  7. }  

第二種:同步延遲加載

Java代碼
  1. public class Singleton {   
  2.  private static Singleton instance = null;   
  3.  private Singleton() {}   
  4.  public static synchronized Singleton getInstance() {   
  5.   if (instance == null) {   
  6.    instance = new Singleton();   
  7.   }   
  8.   return instance;   
  9.  }   
  10. }  

第三種:雙重檢測(cè)同步延遲加載
為處理原版非延遲加載方式瓶頸問(wèn)題,我們需要對(duì) instance 進(jìn)行第二次檢查,目的是避開(kāi)過(guò)多的同步(因?yàn)檫@里的同步只需在第一次創(chuàng)建實(shí)例時(shí)才同步,一旦創(chuàng)建成功,以后獲取實(shí)例時(shí)就不需要同獲取鎖了),但在Java中行不通,因?yàn)橥綁K外面的if (instance == null)可能看到已存在,但不完整的實(shí)例。JDK5.0以后版本若instance為volatile則可行:

Java代碼
  1. public class Singleton {   
  2.  private volatile static Singleton instance = null;   
  3.  private Singleton() {}   
  4.  public static Singleton getInstance() {   
  5.   if (instance == null) {   
  6.    synchronized (Singleton.class) {// 1   
  7.     if (instance == null) {// 2   
  8.      instance = new Singleton();// 3   
  9.     }   
  10.    }   
  11.   }   
  12.   return instance;   
  13.  }   
  14. }  

雙重檢測(cè)鎖定失敗的問(wèn)題并不歸咎于 JVM 中的實(shí)現(xiàn) bug,而是歸咎于 Java 平臺(tái)內(nèi)存模型。內(nèi)存模型允許所謂的“無(wú)序?qū)懭?#8221;,這也是失敗的一個(gè)主要原因。

 無(wú)序?qū)懭?/strong>
為解釋該問(wèn)題,需要重新考察上述清單中的 //3 行。此行代碼創(chuàng)建了一個(gè) Singleton 對(duì)象并初始化變量 instance 來(lái)引用此對(duì)象。這行代碼的問(wèn)題是:在 Singleton 構(gòu)造函數(shù)體執(zhí)行之前,變量 instance 可能成為非 null 的,即賦值語(yǔ)句在對(duì)象實(shí)例化之前調(diào)用,此時(shí)別的線程得到的是一個(gè)還會(huì)初始化的對(duì)象,這樣會(huì)導(dǎo)致系統(tǒng)崩潰。
什么?這一說(shuō)法可能讓您始料未及,但事實(shí)確實(shí)如此。在解釋這個(gè)現(xiàn)象如何發(fā)生前,請(qǐng)先暫時(shí)接受這一事實(shí),我們先來(lái)考察一下雙重檢查鎖定是如何被破壞的。假設(shè)代碼執(zhí)行以下事件序列:

1、線程 1 進(jìn)入 getInstance() 方法。
2、由于 instance 為 null,線程 1 在 //1 處進(jìn)入 synchronized 塊。
3、線程 1 前進(jìn)到 //3 處,但在構(gòu)造函數(shù)執(zhí)行之前,使實(shí)例成為非 null。
4、線程 1 被線程 2 預(yù)占。
5、線程 2 檢查實(shí)例是否為 null。因?yàn)閷?shí)例不為 null,線程 2 將 instance 引用返回給一個(gè)構(gòu)造完整但部分初始化了的 Singleton 對(duì)象。
6、線程 2 被線程 1 預(yù)占。
7、線程 1 通過(guò)運(yùn)行 Singleton 對(duì)象的構(gòu)造函數(shù)并將引用返回給它,來(lái)完成對(duì)該對(duì)象的初始化。
為展示此事件的發(fā)生情況,假設(shè)代碼行 instance =new Singleton(); 執(zhí)行了下列偽代碼:
mem = allocate();             //為單例對(duì)象分配內(nèi)存空間.
instance = mem;               //注意,instance 引用現(xiàn)在是非空,但還未初始化
ctorSingleton(instance);    //為單例對(duì)象通過(guò)instance調(diào)用構(gòu)造函數(shù)
 
這段偽代碼不僅是可能的,而且是一些 JIT 編譯器上真實(shí)發(fā)生的。執(zhí)行的順序是顛倒的,但鑒于當(dāng)前的內(nèi)存模型,這也是允許發(fā)生的。JIT 編譯器的這一行為使雙重檢查鎖定的問(wèn)題只不過(guò)是一次學(xué)術(shù)實(shí)踐而已。

如果真像這篇文章:http://dev.csdn.net/author/axman/4c46d233b388419e9d8b025a3c507b17.html所說(shuō)那樣的話,1.2或以后的版本就不會(huì)有問(wèn)題了,但這個(gè)規(guī)則是JMM的規(guī)范嗎?誰(shuí)能夠確認(rèn)一下。
確實(shí),在JAVA2(以jdk1.2開(kāi)始)以前對(duì)于實(shí)例字段是直接在主儲(chǔ)區(qū)讀寫(xiě)的.所以當(dāng)一個(gè)線程對(duì)resource進(jìn)行分配空間,
初始化和調(diào)用構(gòu)造方法時(shí),可能在其它線程中分配空間動(dòng)作可見(jiàn)了,而初始化和調(diào)用構(gòu)造方法還沒(méi)有完成.

但是從JAVA2以后,JMM發(fā)生了根本的改變,分配空間,初始化,調(diào)用構(gòu)造方法只會(huì)在線程的工作存儲(chǔ)區(qū)完成,在沒(méi)有
向主存儲(chǔ)區(qū)復(fù)制賦值時(shí),其它線程絕對(duì)不可能見(jiàn)到這個(gè)過(guò)程.
而這個(gè)字段復(fù)制到主存區(qū)的過(guò)程,更不會(huì)有分配空間后
沒(méi)有初始化或沒(méi)有調(diào)用構(gòu)造方法的可能.在JAVA中,一切都是按引用的值復(fù)制的.向主存儲(chǔ)區(qū)同步其實(shí)就是把線程工作
存儲(chǔ)區(qū)的這個(gè)已經(jīng)構(gòu)造好的對(duì)象有壓縮堆地址值COPY給主存儲(chǔ)區(qū)的那個(gè)變量.這個(gè)過(guò)程對(duì)于其它線程,要么是resource
為null,要么是完整的對(duì)象.絕對(duì)不會(huì)把一個(gè)已經(jīng)分配空間卻沒(méi)有構(gòu)造好的對(duì)象讓其它線程可見(jiàn).

另一篇詳細(xì)分析文章:http://www.javaeye.com/topic/260515

第四種:使用ThreadLocal修復(fù)雙重檢測(cè)

借助于ThreadLocal,將臨界資源(需要同步的資源)線程局部化,具體到本例就是將雙重檢測(cè)的第一層檢測(cè)條件 if (instance == null) 轉(zhuǎn)換為了線程局部范圍內(nèi)來(lái)作。這里的ThreadLocal也只是用作標(biāo)示而已,用來(lái)標(biāo)示每個(gè)線程是否已訪問(wèn)過(guò),如果訪問(wèn)過(guò),則不再需要走同步塊,這樣就提高了一定的效率。但是ThreadLocal在1.4以前的版本都較慢,但這與volatile相比卻是安全的。

Java代碼
  1. public class Singleton {   
  2.  private static final ThreadLocal perThreadInstance = new ThreadLocal();   
  3.  private static Singleton singleton ;   
  4.  private Singleton() {}   
  5.     
  6.  public static Singleton  getInstance() {   
  7.   if (perThreadInstance.get() == null){   
  8.    // 每個(gè)線程第一次都會(huì)調(diào)用   
  9.    createInstance();   
  10.   }   
  11.   return singleton;   
  12.  }   
  13.   
  14.  private static  final void createInstance() {   
  15.   synchronized (Singleton.class) {   
  16.    if (singleton == null){   
  17.     singleton = new Singleton();   
  18.    }   
  19.   }   
  20.   perThreadInstance.set(perThreadInstance);   
  21.  }   
  22. }  

 
第五種:使用內(nèi)部類實(shí)現(xiàn)延遲加載
為了做到真真的延遲加載,雙重檢測(cè)在Java中是行不通的,所以只能借助于另一類的類加載加延遲加載:

Java代碼
  1. public class Singleton {   
  2.  private Singleton() {}   
  3.  public static class Holder {   
  4.   // 這里的私有沒(méi)有什么意義   
  5.   /* private */static Singleton instance = new Singleton();   
  6.  }   
  7.  public static Singleton getInstance() {   
  8.   // 外圍類能直接訪問(wèn)內(nèi)部類(不管是否是靜態(tài)的)的私有變量   
  9.   return Holder.instance;   
  10.  }   
  11. }  

單例測(cè)試

下面是測(cè)試單例的框架,采用了類加載器與反射。
注,為了測(cè)試單便是否為真真的單例,我自己寫(xiě)了一個(gè)類加載器,且其父加載器設(shè)置為根加載器,這樣確保Singleton由MyClassLoader加載,如果不設(shè)置為根加載器為父加載器,則默認(rèn)為系統(tǒng)加載器,則Singleton會(huì)由系統(tǒng)加載器去加載,但這樣我們無(wú)法卸載類加載器,如果加載Singleton的類加載器卸載不掉的話,那么第二次就不能重新加載Singleton的Class了,這樣Class不能得加載則最終導(dǎo)致Singleton類中的靜態(tài)變量重新初始化,這樣就無(wú)法測(cè)試了。
下面測(cè)試類延遲加載的結(jié)果是可行的,同樣也可用于其他單例的測(cè)試:

Java代碼
  1. public class Singleton {   
  2.  private Singleton() {}   
  3.   
  4.  public static class Holder {   
  5.   // 這里的私有沒(méi)有什么意義   
  6.   /* private */static Singleton instance = new Singleton();   
  7.  }   
  8.   
  9.  public static Singleton getInstance() {   
  10.   // 外圍類能直接訪問(wèn)內(nèi)部類(不管是否是靜態(tài)的)的私有變量   
  11.   return Holder.instance;   
  12.  }   
  13. }   
  14.   
  15. class CreateThread extends Thread {   
  16.  Object singleton;   
  17.  ClassLoader cl;   
  18.   
  19.  public CreateThread(ClassLoader cl) {   
  20.   this.cl = cl;   
  21.  }   
  22.   
  23.  public void run() {   
  24.   Class c;   
  25.   try {   
  26.    c = cl.loadClass("Singleton");   
  27.    // 當(dāng)兩個(gè)不同命名空間內(nèi)的類相互不可見(jiàn)時(shí),可采用反射機(jī)制來(lái)訪問(wèn)對(duì)方實(shí)例的屬性和方法   
  28.    Method m = c.getMethod("getInstance"new Class[] {});   
  29.    // 調(diào)用靜態(tài)方法時(shí),傳遞的第一個(gè)參數(shù)為class對(duì)象   
  30.    singleton = m.invoke(c, new Object[] {});   
  31.    c = null;   
  32.    cl = null;   
  33.   } catch (Exception e) {   
  34.    e.printStackTrace();   
  35.   }   
  36.  }   
  37. }   
  38.   
  39. class MyClassLoader extends ClassLoader {   
  40.  private String loadPath;   
  41.  MyClassLoader(ClassLoader cl) {   
  42.   super(cl);   
  43.  }   
  44.  public void setPath(String path) {   
  45.   this.loadPath = path;   
  46.  }   
  47.  protected Class findClass(String className) throws ClassNotFoundException {   
  48.   FileInputStream fis = null;   
  49.   byte[] data = null;   
  50.   ByteArrayOutputStream baos = null;   
  51.   
  52.   try {   
  53.    fis = new FileInputStream(new File(loadPath   
  54.      + className.replaceAll("\\.""\\\\") + ".class"));   
  55.    baos = new ByteArrayOutputStream();   
  56.    int tmpByte = 0;   
  57.    while ((tmpByte = fis.read()) != -1) {   
  58.     baos.write(tmpByte);   
  59.    }   
  60.    data = baos.toByteArray();   
  61.   } catch (IOException e) {   
  62.    throw new ClassNotFoundException("class is not found:" + className,   
  63.      e);   
  64.   } finally {   
  65.    try {   
  66.     if (fis != null) {   
  67.      fis.close();   
  68.     }   
  69.     if (fis != null) {   
  70.      baos.close();   
  71.     }   
  72.   
  73.    } catch (Exception e) {   
  74.     e.printStackTrace();   
  75.    }   
  76.   }   
  77.   return defineClass(className, data, 0, data.length);   
  78.  }   
  79. }   
  80.   
  81. class SingleTest {   
  82.  public static void main(String[] args) throws Exception {   
  83.   while (true) {   
  84.    // 不能讓系統(tǒng)加載器直接或間接的成為父加載器   
  85.    MyClassLoader loader = new MyClassLoader(null);   
  86.    loader   
  87.      .setPath("D:\\HW\\XCALLC16B125SPC003_js\\uniportal\\service\\AAA\\bin\\");   
  88.    CreateThread ct1 = new CreateThread(loader);   
  89.    CreateThread ct2 = new CreateThread(loader);   
  90.    ct1.start();   
  91.    ct2.start();   
  92.    ct1.join();   
  93.    ct2.join();   
  94.    if (ct1.singleton != ct2.singleton) {   
  95.     System.out.println(ct1.singleton + " " + ct2.singleton);   
  96.    }   
  97.    // System.out.println(ct1.singleton + " " + ct2.singleton);   
  98.    ct1.singleton = null;   
  99.    ct2.singleton = null;   
  100.    Thread.yield();   
  101.   }   
  102.  }   
  103. }  
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服