<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 167,  comments - 30,  trackbacks - 0

    Abstract

             在開發中,如果某個實例的創建需要消耗很多系統資源,那么我們通常會使用惰性加載機制,也就是說只有當使用到這個實例的時候才會創建這個實例,這個好處在單例模式中得到了廣泛應用。這個機制在single-threaded環境下的實現非常簡單,然而在multi-threaded環境下卻存在隱患。本文重點介紹惰性加載機制以及其在多線程環境下的使用方法。(作者numberzero,參考IBM文章《Double-checked locking and the Singleton pattern》,歡迎轉載與討論)

    1       單例模式的惰性加載
    通常當我們設計一個單例類的時候,會在類的內部構造這個類(通過構造函數,或者在定義處直接創建),并對外提供一個static getInstance方法提供獲取該單例對象的途徑。例如:

    Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20Singleton%20%20%20%20%20%20%0A%7B%20%20%20%20%20%20%0A%20%20%20%20private%20static%20Singleton%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%0A%20%20%20%20private%20Singleton()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%E2%80%A6%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
    public   class  Singleton        
    {        
        private   static  Singleton instance =  new  Singleton();        
        private  Singleton(){        
            …        
        }        
        public   static  Singleton getInstance(){        
                 return  instance;         
        }        
    }       
    public class Singleton     
    {     
        private static Singleton instance = new Singleton();     
        private Singleton(){     
            …     
        }     
        public static Singleton getInstance(){     
                 return instance;      
        }     
    }    

             這樣的代碼缺點是:第一次加載類的時候會連帶著創建Singleton實例,這樣的結果與我們所期望的不同,因為創建實例的時候可能并不是我們需要這個實例的時候。同時如果這個Singleton實例的創建非常消耗系統資源,而應用始終都沒有使用Singleton實例,那么創建Singleton消耗的系統資源就被白白浪費了。

             為了避免這種情況,我們通常使用惰性加載的機制,也就是在使用的時候才去創建。以上代碼的惰性加載代碼如下:

    Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20Singleton%7B%20%20%20%20%20%20%0A%20%20%20%20private%20static%20Singleton%20instance%20%3D%20null%3B%20%20%20%20%20%20%0A%20%20%20%20private%20Singleton()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%E2%80%A6%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
    public   class  Singleton{        
        private   static  Singleton instance =  null ;        
        private  Singleton(){        
            …        
        }        
        public   static  Singleton getInstance(){        
            if  (instance ==  null )        
                instance = new  Singleton();         
                    return  instance;         
        }        
    }       
    public class Singleton{     
        private static Singleton instance = null;     
        private Singleton(){     
            …     
        }     
        public static Singleton getInstance(){     
            if (instance == null)     
                instance = new Singleton();      
                    return instance;      
        }     
    }    
     
             這樣,當我們第一次調用Singleton.getInstance()的時候,這個單例才被創建,而以后再次調用的時候僅僅返回這個單例就可以了。

    2       惰性加載在多線程中的問題
    先將惰性加載的代碼提取出來:                            

    Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%20%0A%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%7D%20%20%20%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
    public   static  Singleton getInstance(){        
        if  (instance ==  null )        
        instance = new  Singleton();         
        return  instance;         
    }      
    public static Singleton getInstance(){     
        if (instance == null)     
        instance = new Singleton();      
        return instance;      
    }             這是如果兩個線程A和B同時執行了該方法,然后以如下方式執行:

    1.         A進入if判斷,此時foo為null,因此進入if內

    2.         B進入if判斷,此時A還沒有創建foo,因此foo也為null,因此B也進入if內

    3.         A創建了一個Foo并返回

    4.         B也創建了一個Foo并返回

    此時問題出現了,我們的單例被創建了兩次,而這并不是我們所期望的。

    3       各種解決方案及其存在的問題
    3.1     使用Class鎖機制
    以上問題最直觀的解決辦法就是給getInstance方法加上一個synchronize前綴,這樣每次只允許一個現成調用getInstance方法:

    Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20synchronized%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%20%0A%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
    public   static   synchronized  Singleton getInstance(){        
        if  (instance ==  null )        
        instance = new  Singleton();         
        return  instance;         
    }       
    public static synchronized Singleton getInstance(){     
        if (instance == null)     
        instance = new Singleton();      
        return instance;      
    }    
        這種解決辦法的確可以防止錯誤的出現,但是它卻很影響性能:每次調用getInstance方法的時候都必須獲得Singleton的鎖,而實際上,當單例實例被創建以后,其后的請求沒有必要再使用互斥機制了

    3.2     double-checked locking
    曾經有人為了解決以上問題,提出了double-checked locking的解決方案

    Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20synchronized(instance)%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
    public   static  Singleton getInstance(){        
        if  (instance ==  null )        
            synchronized (instance){        
                if (instance ==  null )        
                    instance = new  Singleton();        
            }        
        return  instance;         
    }  
    public static Singleton getInstance(){     
        if (instance == null)     
            synchronized(instance){     
                if(instance == null)     
                    instance = new Singleton();     
            }     
        return instance;      
    }         讓我們來看一下這個代碼是如何工作的:首先當一個線程發出請求后,會先檢查instance是否為null,如果不是則直接返回其內容,這樣避免了進入 synchronized塊所需要花費的資源。其次,即使第2節提到的情況發生了,兩個線程同時進入了第一個if判斷,那么他們也必須按照順序執行 synchronized塊中的代碼,第一個進入代碼塊的線程會創建一個新的Singleton實例,而后續的線程則因為無法通過if判斷,而不會創建多余的實例。

             上述描述似乎已經解決了我們面臨的所有問題,但實際上,從JVM的角度講,這些代碼仍然可能發生錯誤。

             對于JVM而言,它執行的是一個個Java指令。在Java指令中創建對象和賦值操作是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM并不保證這兩個操作的先后順序,也就是說有可能JVM會為新的Singleton實例分配空間,然后直接賦值給instance成員,然后再去初始化這個Singleton實例。這樣就使出錯成為了可能,我們仍然以A、B兩個線程為例:

    1.         A、B線程同時進入了第一個if判斷

    2.         A首先進入synchronized塊,由于instance為null,所以它執行instance = new Singleton();

    3.         由于JVM內部的優化機制,JVM先畫出了一些分配給Singleton實例的空白內存,并賦值給instance成員(注意此時JVM沒有開始初始化這個實例),然后A離開了synchronized塊。

    4.         B進入synchronized塊,由于instance此時不是null,因此它馬上離開了synchronized塊并將結果返回給調用該方法的程序。

    5.         此時B線程打算使用Singleton實例,卻發現它沒有被初始化,于是錯誤發生了。

    4       通過內部類實現多線程環境中的單例模式
    為了實現慢加載,并且不希望每次調用getInstance時都必須互斥執行,最好并且最方便的解決辦法如下:

    Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20Singleton%7B%20%20%20%20%20%20%0A%20%20%20%20private%20Singleton()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%E2%80%A6%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20private%20static%20class%20SingletonContainer%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20private%20static%20Singleton%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20return%20SingletonContainer.instance%3B%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
    public   class  Singleton{        
        private  Singleton(){        
            …        
        }        
        private   static   class  SingletonContainer{        
            private   static  Singleton instance =  new  Singleton();        
        }        
        public   static  Singleton getInstance(){        
            return  SingletonContainer.instance;        
        }        
    }       
    public class Singleton{     
        private Singleton(){     
            …     
        }     
        private static class SingletonContainer{     
            private static Singleton instance = new Singleton();     
        }     
        public static Singleton getInstance(){     
            return SingletonContainer.instance;     
        }     
    }    
           JVM內部的機制能夠保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當我們第一次調用getInstance的時候,JVM能夠幫我們保證instance只被創建一次,并且會保證把賦值給instance的內存初始化完畢,這樣我們就不用擔心3.2中的問題。此外該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了3.1中的低效問題。最后instance是在第一次加載SingletonContainer類時被創建的,而 SingletonContainer類則在調用getInstance方法的時候才會被加載,因此也實現了惰性加載。

     

    本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/fancyerII/archive/2010/03/15/5382349.aspx

    posted on 2010-05-14 14:17 David1228 閱讀(467) 評論(0)  編輯  收藏 所屬分類: 線程-模式-設計

    <2010年5月>
    2526272829301
    2345678
    9101112131415
    16171819202122
    23242526272829
    303112345

    常用鏈接

    留言簿(4)

    隨筆分類

    隨筆檔案

    文章檔案

    新聞分類

    新聞檔案

    相冊

    收藏夾

    Java

    Linux知識相關

    Spring相關

    云計算/Linux/虛擬化技術/

    友情博客

    多線程并發編程

    開源技術

    持久層技術相關

    搜索

    •  

    積分與排名

    • 積分 - 358624
    • 排名 - 154

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲精品天堂成人片AV在线播放 | 免费一级毛片在线观看| 亚洲一线产区二线产区精华| 99久久国产免费-99久久国产免费| 亚洲人成色777777在线观看| 成全视成人免费观看在线看| 亚洲国产精品无码久久久秋霞2| 国产真人无码作爱视频免费| 亚洲一区中文字幕久久| 无码国产精品一区二区免费| 亚洲国产最大av| 国产成人免费片在线视频观看| 国产亚洲精品精品精品| 中文字幕亚洲一区| a色毛片免费视频| 亚洲色偷偷av男人的天堂| 免费影院未满十八勿进网站| 亚洲色精品三区二区一区| 亚洲 国产 图片| 亚洲人成网7777777国产| 国产成人免费ā片在线观看老同学| 午夜寂寞在线一级观看免费| 99亚洲乱人伦aⅴ精品| 亚洲香蕉成人AV网站在线观看| 国产自国产自愉自愉免费24区| 91亚洲va在线天线va天堂va国产| 无码少妇一区二区浪潮免费| 黄页视频在线观看免费| 亚洲国产精品无码久久久不卡 | 免费永久看黄在线观看app| 国产99久久久久久免费看| 亚洲第一精品在线视频| 成人午夜18免费看| 三年片免费观看大全国语| 亚洲国产精品成人久久久 | 999国内精品永久免费观看| 国产精品亚洲va在线观看| 亚洲VA中文字幕无码一二三区| 成人免费看片又大又黄| 在线观看免费视频网站色| 亚洲色成人四虎在线观看|