程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 關於單態

關於單態

編輯:關於JAVA

單態定義:

  Singleton模式主要作用是保證在Java應用程序中,一個類Class只有一個實例存在。

  Singleton模式就為我們提供了這樣實現的可能。使用Singleton的好處還在於可以節省內存,因為它限制了實例的個數,有利於Java垃圾回收(garbage collection)。

  使用Singleton注意事項:

  有時在某些情況下,使用Singleton並不能達到Singleton的目的,如有多個Singleton對象同時被不同的類裝入器裝載;在EJB這樣的分布式系統中使用也要注意這種情況,因為EJB是跨服務器,跨JVM的

  單態模式的演化:

  單態模式是個簡單的模式,但是這個簡單的模式也有很多復雜的東西。

  (注意:在這裡補充一下,現在單態模式其實有一個寫法是不錯的見這裡:http://www.blogJava.Net/dreamstone /archive/2007/02/27/101000.Html,但還是建議看完這篇文章,因為解釋的事情是不一樣的,這裡說的是為什麼double- checked不能使用。)

  一,首先最簡單的單態模式,單態模式1

  import Java.util.*;

  class Singleton

  {

  private static Singleton instance;

  private Vector v;

  private boolean inUse;

  private Singleton()

  {

  v = new Vector();

  v.addElement(new Object());

  inUse = true;

  }

  public static Singleton getInstance()

  {

  if (instance == null) //1

  instance = new Singleton(); //2

  return instance; //3

  }

  }

  這個單態模式是不安全的,為什麼說呢 ?因為沒考慮多線程,如下情況

  Thread 1 調用getInstance() 方法,並且判斷instance是null,然後進入if模塊,

  在實例化instance之前,

  Thread 2搶占了Thread 1的cpu

  Thread 2 調用getInstance() 方法,並且判斷instance是null,然後進入if模塊,

  Thread 2 實例化instance 完成,返回

  Thread 1 再次實例化instance

  這個單態已經不在是單態

  二,為了解決剛才的問題:單態模式2

  public static synchronized Singleton getInstance()

  {

  if (instance == null) //1

  instance = new Singleton(); //2

  return instance; //3

  }

 采用同步來解決,這種方式解決了問題,但是仔細分析正常的情況下只有第一次時候,進入對象的實例化,須要同步,其它時候都是直接返回已經實例化好的instance不須要同步,大家都知到在一個多線程的程序中,如果同步的消耗是很大的,很容易造成瓶頸

  三,為了解決上邊的問題:單態模式3,加入同步

  public static Singleton getInstance()

  {

  if (instance == null)

  {

  synchronized(Singleton.class) {

  instance = new Singleton();

  }

  }

  return instance;

  }

  同步改成塊同步,而不使用函數同步,但是仔細分析,

  又回到了模式一的狀態,再多線程的時候根本沒有解決問題

  四,為了對應上邊的問題:單態模式4,也就是很多人采用的Double-checked locking

  public static Singleton getInstance()

  {

  if (instance == null)

  {

  synchronized(Singleton.class) { //1

  if (instance == null) //2

  instance = new Singleton(); //3

  }

  }

  return instance;

  }

  這樣,模式一中提到的問題解決了。不會出現多次實例化的現象

  當第一次進入的時候,保正實例化時候的單態,在實例化後,多線程訪問的時候直接返回,不須要進入同步模塊,既實現了單態,又沒有損失性能。表面上看我們的問題解決了,但是再仔細分析:

  我們來假象這中情況:

  Thread 1 :進入到//3位置,執行new Singleton(),但是在構造函數剛剛開始的時候被Thread2搶占cpu

  Thread 2 :進入getInstance(),判斷instance不等於null,返回instance,

  (instance已經被new,已經分配了內存空間,但是沒有初始化數據)

  Thread 2 :利用返回的instance做某些操做,失敗或者異常

  Thread 1 :取得cpu初始化完成

  過程中可能有多個線程取到了沒有完成的實例,並用這個實例作出某些操做。

  -----------------------------------------

  出現以上的問題是因為

  mem = allocate(); //分配內存

  instance = mem; //標記instance非空

  //未執行構造函數,thread 2從這裡進入

  ctorSingleton(instance); //執行構造函數

  //返回instance

 

五,證明上邊的假想是可能發生的,字節碼是用來分析問題的最好的工具,可以利用它來分析下邊一段程序:(為了分析方便,所以漸少了內容)

  字節碼的使用方法見這裡,利用字節碼分析問題

  class Singleton

  {

  private static Singleton instance;

  private boolean inUse;

  private int val;

  private Singleton()

  {

  inUse = true;

  val = 5;

  }

  public static Singleton getInstance()

  {

  if (instance == null)

  instance = new Singleton();

  return instance;

  }

  }

  得到的字節碼

  ;asm code generated for getInstance

  054D20B0 mov eax,[049388C8] ;load instance ref

  054D20B5 test eax,eax ;test for null

  054D20B7 jne 054D20D7

  054D20B9 mov eax,14C0988h

  054D20BE call 503EF8F0 ;allocate memory

  054D20C3 mov [049388C8],eax ;store pointer in

  ;instance ref. instance

  ;non-null and ctor

  ;has not run

  054D20C8 mov ecx,dWord ptr [eax]

  054D20CA mov dWord ptr [ecx],1 ;inline ctor - inUse=true;

  054D20D0 mov dWord ptr [ecx+4],5 ;inline ctor - val=5;

  054D20D7 mov ebx,dWord ptr ds:[49388C8h]

  054D20DD jmp 054D20B0

  上邊的字節碼證明,猜想是有可能實現的

  六:好了,上邊證明Double-checked locking可能出現取出錯誤數據的情況,那麼我們還是可以解決的

  public static Singleton getInstance()

  {

  if (instance == null)

  {

  synchronized(Singleton.class) { //1

  Singleton inst = instance; //2

  if (inst == null)

  {

  synchronized(Singleton.class) { //3

  inst = new Singleton(); //4

  }

  instance = inst; //5

  }

  }

  }

  return instance;

  }

  利用Double-checked locking 兩次同步,中間變量,解決上邊的問題。

  (下邊這段話我只能簡單的理解,翻譯過來不好,所以保留原文,list 7是上邊的代碼,list 8是下邊的

  The code in Listing 7 doesn‘t work because of the current definition of the memory model.

  The Java Language Specification (JLS) demands that code within a synchronized block

  not be moved out of a synchronized block. However, it does not say that

  code not in a synchronized block cannot be moved into a synchronized block.

  A JIT compiler would see an optimization opportunity here.

  This optimization would remove the code at

  //4 and the code at //5, combine it and generate the code shown in Listing 8:)

  -------------------------------------------------

  list 8

  public static Singleton getInstance()

  {

  if (instance == null)

  {

  synchronized(Singleton.class) { //1

  Singleton inst = instance; //2

  if (inst == null)

  {

  synchronized(Singleton.class) { //3

  //inst = new Singleton(); //4

  instance = new Singleton();

  }

  //instance = inst; //5

  }

  }

  }

  return instance;

  }

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved