溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在Java中使用單例模式

發(fā)布時(shí)間:2021-05-12 16:53:19 來(lái)源:億速云 閱讀:155 作者:Leah 欄目:編程語(yǔ)言

怎么在Java中使用單例模式?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

單例模式的應(yīng)用場(chǎng)景:

  單例模式(Singleton Pattern)是指確保一個(gè)類在任何情況下都絕對(duì)只有一個(gè)實(shí)例。并提供一個(gè)全局反訪問(wèn)點(diǎn)。單例模式是創(chuàng)建型模式。單例模式在生活中應(yīng)用也很廣泛,比如公司CEO只有一個(gè),部門(mén)經(jīng)理只有一個(gè)等。JAVA中ServletCOntext,ServetContextCOnfig等,還有spring中ApplicationContext應(yīng)用上下文對(duì)象,SessionFactory,數(shù)據(jù)庫(kù)連接池對(duì)象等。使用單例模式可以將其常駐于內(nèi)存,可以節(jié)約更多資源。

寫(xiě)法:

  1:懶漢模式(線程不安全)

/**
 * 線程不安全的懶漢式單利模式
 * 
 * Created by gan on 2019/11/17 17:33.
 */
public class LazySingleton {
  private static LazySingleton instance;

  //構(gòu)造方法私有化
  private LazySingleton() {
  }

  public static LazySingleton getInstance() {
    if (instance != null) {
      instance = new LazySingleton();
    }
    return instance;
  }
}

  上面的代碼,提供一個(gè)靜態(tài)對(duì)象instance,構(gòu)造函數(shù)私有化防止外部創(chuàng)建對(duì)象,提供一個(gè)靜態(tài)的getInstance方法來(lái)給訪問(wèn)者一個(gè)單例對(duì)象。這種寫(xiě)法的缺點(diǎn)就是沒(méi)有考慮到線程安全問(wèn)題,當(dāng)多個(gè)訪問(wèn)者同時(shí)訪問(wèn)的時(shí)候很有可能創(chuàng)建多個(gè)對(duì)象。之所以叫懶漢式,是因?yàn)檫@種寫(xiě)法是使用的時(shí)候才創(chuàng)建,起到了懶加載Lazy loading的作用,實(shí)際開(kāi)發(fā)中不建議采用這種寫(xiě)法。

  2:線程安全的懶漢式(加鎖)

/**
 * 線程安全的懶漢式單利模式
 * 
 * Created by gan on 2019/11/17 17:33.
 */
public class LazySingleton {
  private static LazySingleton instance;

  //構(gòu)造方法私有化
  private LazySingleton() {
  }

  public synchronized static LazySingleton getInstance() {
    if (instance != null) {
      instance = new LazySingleton();
    }
    return instance;
  }
}

  這種寫(xiě)法就是在第一種的基礎(chǔ)上添加了synchronized關(guān)鍵字保證了線程安全。這種寫(xiě)法在并發(fā)高的時(shí)候雖然保證了線程安全,但是效率很低,高并發(fā)的時(shí)候所有訪問(wèn)的線程都要排隊(duì)等待,所以實(shí)際開(kāi)發(fā)中也不建議采用。

  3:惡漢式(線程安全)

/**
 * 餓漢式(線程安全)
 * Created by gan on 2019/10/28 22:52.
 */
public class HungrySigleton {

  public static final HungrySigleton instance = new HungrySigleton();

  private HungrySigleton(){}

  public static HungrySigleton getInstance(){
    return instance;
  }
}

  直接在運(yùn)行(加載)這個(gè)類的時(shí)候創(chuàng)建了對(duì)象,之后直接訪問(wèn)。顯然這種方式?jīng)]有起到Lazy loading的效果。但是是線程安全的,實(shí)際開(kāi)發(fā)中還是比較常用。

  4:靜態(tài)內(nèi)部類(線程安全)

/**
 * 靜態(tài)內(nèi)部類方式
 * Created by gan on 2019/11/17 17:46.
 */
public class StaticInnerClassSingleton {

  //構(gòu)造方法私有化
  private StaticInnerClassSingleton() {}

  //內(nèi)部類
  private static class HolderInnerClass {
    //需要提供單利對(duì)象的外部類作為靜態(tài)屬性加載的時(shí)候就初始化
    private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
  }

  //對(duì)外暴漏訪問(wèn)點(diǎn)
  public static StaticInnerClassSingleton getInstance() {
    return HolderInnerClass.instance;
  }
}

  這種內(nèi)部類跟餓漢式單例有很多相似的地方,相比餓漢式單例模式的區(qū)別也是好處在于:靜態(tài)內(nèi)部類不在單例類加載時(shí)就加載,而是在調(diào)用getInstance()方法的時(shí)候才進(jìn)行加載,達(dá)到了類似于懶漢式的效果,而且這種方法又是線程安全的。實(shí)際開(kāi)發(fā)中也建議采用。

  5:枚舉方法單例(線程安全)

/**
 * 枚舉單利模式
 * Created by gan on 2019/11/17 17:57.
 */
public enum EnumSingleton {
  INSTANCE;

  public void otherMetthod() {
    System.out.println("需要單利對(duì)象調(diào)用的方法。。。");
  }
}

  Effective Java作者Josh Bloch提倡的方式,好處有如下:

  1:自由串行化。

  2:保證了一個(gè)實(shí)例

  3:線程安全

  這種方式防止了單例模式被破壞,而且簡(jiǎn)潔寫(xiě)法簡(jiǎn)單,而且絕對(duì)的線程安全,但是有個(gè)缺點(diǎn)就是不能繼承。

  6:雙重檢查法(通常線程安全,低概率不安全)

/**
 * Double check
 * Created by gan on 2019/11/17 18:03.
 */
public class DoubleCheckSingleton {
  private static DoubleCheckSingleton instance;

  private DoubleCheckSingleton() {}

  public static DoubleCheckSingleton getInstance() {
    if (instance == null) {
      synchronized (DoubleCheckSingleton.class) {
        if (instance == null) {
          instance = new DoubleCheckSingleton();
        }
      }
    }
    return instance;
  }
}

  上面的這種寫(xiě)法在并發(fā)極高的時(shí)候也可能會(huì)出現(xiàn)問(wèn)題(當(dāng)然這種概率非常小,但是畢竟還是有的嘛),解決的方案就是給instance的聲明加上volatile關(guān)鍵字即可。于是就出現(xiàn)了下面第7總寫(xiě)法。

  7:Double check(volatile)

/**
 * Double check volatile
 * Created by gan on 2019/11/17 18:03.
 */
public class DoubleCheckSingleton {
  private volatile static DoubleCheckSingleton instance;

  private DoubleCheckSingleton() {}

  public static DoubleCheckSingleton getInstance() {
    if (instance == null) {
      synchronized (DoubleCheckSingleton.class) {
        if (instance == null) {
          instance = new DoubleCheckSingleton();
        }
      }
    }
    return instance;
  }
}

volatile關(guān)鍵字的其中一個(gè)作用就是禁止指令重排序,把instance聲明volatile后,對(duì)它的操作就會(huì)有一個(gè)內(nèi)存屏障(什么是內(nèi)存屏障?),這樣在賦值完成之前,就不會(huì)調(diào)用讀操作。這里具體的原因網(wǎng)上也是眾說(shuō)紛紜,這里不進(jìn)行具體闡述。

  8:ThreadLocal實(shí)現(xiàn)單例模式(線程安全)

/**
 * ThreadLocal實(shí)現(xiàn)單利模式
 * Created by gan on 2019/11/17 18:17.
 */
public class ThreadLocalSingleton {

  private static final ThreadLocal<ThreadLocalSingleton> threadLocal = new ThreadLocal() {
    @Override
    protected ThreadLocalSingleton initialValue() {
      return new ThreadLocalSingleton();
    }
  };

  private ThreadLocalSingleton(){}

  public static ThreadLocalSingleton getInstance(){
    return threadLocal.get();
  }
}

  ThreadLocal會(huì)為每個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程堆數(shù)據(jù)的訪問(wèn)沖突。對(duì)于多線程資源共享問(wèn)題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal則采用了“以空間換時(shí)間”的方式(主要就是避免了加鎖排隊(duì))。 前者提供一份變量,讓不同的線程排隊(duì)訪問(wèn),而后者為每一個(gè)線程提供了一份變量,因此可以同時(shí)訪問(wèn)而互不影響。但是實(shí)際是創(chuàng)建了多個(gè)單例對(duì)象的。

單例模式的破壞:

  1:序列化破壞

    一個(gè)對(duì)象創(chuàng)建好以后,有時(shí)候需要將對(duì)象序列化然后寫(xiě)入磁盤(pán)。下次在從磁盤(pán)中讀取并反序列化,將其轉(zhuǎn)化為內(nèi)存對(duì)象。反序列化后的對(duì)象會(huì)重新分配內(nèi)存,即創(chuàng)建型的對(duì)象。這樣就違背了單例模式的初衷。解決這種方式的方法就是在單例類中新增一個(gè) private Object readResolve();方法即可,具體原因可以看看序列化和反序列化的源碼。

  2:反射

    通過(guò)反射“暴力破解”也能破壞單例模式,具體暫時(shí)不闡述。

  3:克隆

    克隆也會(huì)破壞單例模式,具體暫時(shí)不闡述。

Java的特點(diǎn)有哪些

Java的特點(diǎn)有哪些 1.Java語(yǔ)言作為靜態(tài)面向?qū)ο缶幊陶Z(yǔ)言的代表,實(shí)現(xiàn)了面向?qū)ο罄碚?,允許程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程。 2.Java具有簡(jiǎn)單性、面向?qū)ο蟆⒎植际?、安全性、平臺(tái)獨(dú)立與可移植性、動(dòng)態(tài)性等特點(diǎn)。 3.使用Java可以編寫(xiě)桌面應(yīng)用程序、Web應(yīng)用程序、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序等。

看完上述內(nèi)容,你們掌握怎么在Java中使用單例模式的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI