亚洲精品中文字幕无乱码_久久亚洲精品无码AV大片_最新国产免费Av网址_国产精品3级片

java語言

JAVA多線程并發(fā)下的單例模式應用

時間:2024-11-09 00:56:27 java語言 我要投稿
  • 相關推薦

JAVA多線程并發(fā)下的單例模式應用

  單例模式應該是設計模式中比較簡單的一個,也是非常常見的,但是在多線程并發(fā)的環(huán)境下使用卻是不那么簡單了,今天小編給大家分享一個在開發(fā)過程中遇到的單例模式的應用。

  單例模式應該是設計模式中比較簡單的一個,也是非常常見的,但是在多線程并發(fā)的環(huán)境下使用卻是不那么簡單了,今天給大家分享一個我在開發(fā)過程中遇到的單例模式的應用。

  首先我們先來看一下單例模式的定義:

  一個類有且僅有一個實例,并且自行實例化向整個系統(tǒng)提供。

  單例模式的要素:

  1.私有的靜態(tài)的實例對象

  2.私有的構(gòu)造函數(shù)(保證在該類外部,無法通過new的方式來創(chuàng)建對象實例)

  3.公有的、靜態(tài)的、訪問該實例對象的方法

  單例模式分為懶漢形和餓漢式

  懶漢式:

  應用剛啟動的時候,并不創(chuàng)建實例,當外部調(diào)用該類的實例或者該類實例方法的時候,才創(chuàng)建該類的實例。(時間換空間)

  優(yōu)點:實例在被使用的時候才被創(chuàng)建,可以節(jié)省系統(tǒng)資源,體現(xiàn)了延遲加載的思想。

  缺點:由于系統(tǒng)剛啟動時且未被外部調(diào)用時,實例沒有創(chuàng)建;如果一時間有多個線程同時調(diào)用LazySingleton.getLazyInstance()方法很有可能會產(chǎn)生多個實例。

  例子:

  publicclassSingletonClass{

  //私有構(gòu)造函數(shù),保證類不能通過new創(chuàng)建

  privateSingletonClass(){}

  privatestaticSingletonClassinstance=null;

  publicstaticSingletonClassgetInstance(){

  if(instance==null){

  //創(chuàng)建本類對象

  instance=newSingletonClass();

  }

  returninstance;

  }

  }

  餓漢式:

  應用剛啟動的時候,不管外部有沒有調(diào)用該類的實例方法,該類的實例就已經(jīng)創(chuàng)建好了。(空間換時間。)

  優(yōu)點:寫法簡單,在多線程下也能保證單例實例的唯一性,不用同步,運行效率高。

  缺點:在外部沒有使用到該類的時候,該類的實例就創(chuàng)建了,若該類實例的創(chuàng)建比較消耗系統(tǒng)資源,并且外部一直沒有調(diào)用該實例,那么這部分的系統(tǒng)資源的消耗是沒有意義的。

  例子:

  publicclassSingleton{

  //首先自己在內(nèi)部定義自己的一個實例,只供內(nèi)部調(diào)用

  privatestaticfinalSingletoninstance=newSingleton();

  //私有構(gòu)造函數(shù)

  privateSingleton(){

  }

  //提供了靜態(tài)方法,外部可以直接調(diào)用

  publicstaticSingletongetInstance(){

  returninstance;

  }

  }

  下面模擬單例模式在多線程下會出現(xiàn)的問題

  /**

  *懶漢式單例類

  */

  publicclassLazySingleton{

  //為了易于模擬多線程下,懶漢式出現(xiàn)的問題,我們在創(chuàng)建實例的構(gòu)造函數(shù)里面使當前線程暫停了50毫秒

  privateLazySingleton(){

  try{

  Thread.sleep(50);

  }catch(InterruptedExceptione){

  e.printStackTrace();

  }

  System.out.println("生成LazySingleton實例一次!");

  }

  privatestaticLazySingletonlazyInstance=null;

  publicstaticLazySingletongetLazyInstance(){

  if(lazyInstance==null){

  lazyInstance=newLazySingleton();

  }

  returnlazyInstance;

  }

  }

  測試代碼:我們在測試代碼里面新建了10個線程,讓這10個線程同時調(diào)用LazySingleton.getLazyInstance()方法

  publicclassSingletonTest{

  publicstaticvoidmain(String[]args){

  //創(chuàng)建十個線程調(diào)

  for(inti=0;i<10;i++){

  newThread(){

  @Override

  publicvoidrun(){

  LazySingleton.getLazyInstance();

  }

  }.start();

  }

  }

  }

  結(jié)果:

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  生成LazySingleton實例一次!

  可以看出單例模式懶漢式在多線程的并發(fā)下也會出現(xiàn)問題,

  分析一下:多個線程同時訪問上面的懶漢式單例,現(xiàn)在有兩個線程A和B同時訪問LazySingleton.getLazyInstance()方法。

  假設A先得到CPU的時間切片,A執(zhí)行到if(lazyInstance==null)時,由于lazyInstance之前并沒有實例化,所以lazyInstance==null為true,在還沒有執(zhí)行實例創(chuàng)建的時候

  此時CPU將執(zhí)行時間分給了線程B,線程B執(zhí)行到if(lazyInstance==null)時,由于lazyInstance之前并沒有實例化,所以lazyInstance==null為true,線程B繼續(xù)往下執(zhí)行實例的創(chuàng)建過程,線程B創(chuàng)建完實例之后,返回。

  此時CPU將時間切片分給線程A,線程A接著開始執(zhí)行實例的創(chuàng)建,實例創(chuàng)建完之后便返回。由此看線程A和線程B分別創(chuàng)建了一個實例(存在2個實例了),這就導致了單例的失效。

  解決辦法:我們可以在getLazyInstance方法上加上synchronized使其同步,但是這樣一來,會降低整個訪問的速度,而且每次都要判斷。

  那么有沒有更好的方式來實現(xiàn)呢?我們可以考慮使用"雙重檢查加鎖"的方式來實現(xiàn),就可以既實現(xiàn)線程安全,又能夠使性能不受到很大的影響。我們看看具體解決代碼

  publicclassLazySingleton{

  privateLazySingleton(){

  try{

  Thread.sleep(50);

  }catch(InterruptedExceptione){

  e.printStackTrace();

  }

  System.out.println("生成LazySingleton實例一次!");

  }

  privatestaticLazySingletonlazyInstance=null;

  publicstaticLazySingletongetLazyInstance(){

  //先檢查實例是否存在,如果不存在才進入下面的同步塊

  if(lazyInstance==null){

  //同步塊,線程安全地創(chuàng)建實例

  synchronized(LazySingleton.class){

  //再次檢查實例是否存在,如果不存在才真正地創(chuàng)建實例

  if(lazyInstance==null){

  lazyInstance=newLazySingleton();

  }

  }

  }

  returnlazyInstance;

  }

  }

  這樣我們就可以在多線程并發(fā)下安全應用單例模式中的懶漢模式。這種方法在代碼上可能就不怎么美觀,我們可以優(yōu)雅的使用一個內(nèi)部類來維護單例類的實例,下面看看代碼

  publicclassGracefulSingleton{

  privateGracefulSingleton(){

  System.out.println("創(chuàng)建GracefulSingleton實例一次!");

  }

  //類級的內(nèi)部類,也就是靜態(tài)的成員式內(nèi)部類,該內(nèi)部類的實例與外部類的實例沒有綁定關系,而且只有被調(diào)用到才會裝載,從而實現(xiàn)了延遲加載

  privatestaticclassSingletonHoder{

  //靜態(tài)初始化器,由JVM來保證線程安全

  privatestaticGracefulSingletoninstance=newGracefulSingleton();

  }

  publicstaticGracefulSingletongetInstance(){

  returnSingletonHoder.instance;

  }

  }

  說一下我在實際開發(fā)中的場景:為了程序的高效率使用多線程并發(fā),然而是循環(huán)調(diào)用,可能導致創(chuàng)建線程數(shù)過多,考慮采用線程池管理,這時候創(chuàng)建線程池仍然是處于循環(huán)調(diào)用中,也可能導致多個線程池,這時候就考慮使用單例模式。

  源代碼:

  publicclassThreadPoolFactoryUtil{

  privateExecutorServiceexecutorService;

  //在構(gòu)造函數(shù)中創(chuàng)建線程池

  privateThreadPoolFactoryUtil(){

  //獲取系統(tǒng)處理器個數(shù),作為線程池數(shù)量

  intnThreads=Runtime.getRuntime().availableProcessors();

  executorService=Executors.newFixedThreadPool(nThreads);

  }

  //定義一個靜態(tài)內(nèi)部類,內(nèi)部定義靜態(tài)成員創(chuàng)建外部類實例

  privatestaticclassSingletonContainer{

  privatestaticThreadPoolFactoryUtilutil=newThreadPoolFactoryUtil();

  }

  //獲取本類對象

  publicstaticThreadPoolFactoryUtilgetUtil(){

  returnSingletonContainer.util;

  }

  publicExecutorServicegetExecutorService(){

  returnexecutorService;

  }

  }

  涉及到一個靜態(tài)內(nèi)部類,我們看看靜態(tài)內(nèi)部類的特點:

  1、靜態(tài)內(nèi)部類無需依賴于外部類,它可以獨立于外部對象而存在。

  2、靜態(tài)內(nèi)部類,多個外部類的對象可以共享同一個內(nèi)部類的對象。

  3、使用靜態(tài)內(nèi)部類的好處是加強了代碼的封裝性以及提高了代碼的可讀性。

  4、普通內(nèi)部類不能聲明static的方法和變量,注意這里說的是變量,常量(也就是finalstatic修飾的屬性)還是可以的,而靜態(tài)內(nèi)部類形似外部類,沒有任何限制?梢灾苯颖挥猛獠款惷+內(nèi)部類名獲得。

  以上是我在實際開發(fā)中遇到的一些問題,部分摘自網(wǎng)上代碼,結(jié)合實開發(fā)際案例。如有不妥,希望大家及時指出!

【JAVA多線程并發(fā)下的單例模式應用】相關文章:

Java單例設計模式09-27

JAVA設計模式之單例模式07-29

java的多線程09-09

java多線程08-31

java語言的多線程08-29

java多線程教程11-03

java多線程介紹08-23

Java實現(xiàn)多線程的方法11-10

Java使用多線程的優(yōu)勢07-10

Java多線程通信方法09-03