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

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

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

    神奇好望角 The Magical Cape of Good Hope

    庸人不必自擾,智者何需千慮?
    posts - 26, comments - 50, trackbacks - 0, articles - 11
      BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

    2012年2月13日

    21 世紀初,Spring 框架的誕生和崛起讓沉重而腐朽的 J2EE 遭到了當頭棒喝,隨后大批開發人員轉投 Spring 陣營,呼吸間就讓 J2EE 陣營大傷元氣。然而這種命懸一線的危機并沒有造成毀滅性的打擊,尤其是對于 Java 這種提倡開放的平臺而言,取長補短,互相促進才是正道。于是,JCP 委員會痛定思痛,在 2006 年推出 Java EE 5 規范,主要是對 EJB 的開發進行了極大幅度的簡化。2008 年發布的 Java EE 6 引入了 CDI、BV、JAX-RS 等一系列新功能,并且以配置文件(profile)的方式讓 Java EE 向輕量級邁進了一步。特別有趣的是,Spring 框架也開始提供對某些 Java EE 注解的支持,是否標志著兩大陣營開始合流?Java EE 7 預定于今年下半年發布,目標是支持云計算。最近幾年來,云計算一直被炒作,卻從來沒有一個準確的定義和規范,希望 Java EE 7 能夠在 Java 界扭轉這種尷尬的局面。

    下面開始詳細列舉 Java EE 7 的新功能前瞻,數據來源于《Java Magazine 2012-01/02》中的《Cloud/Java EE: Looking Ahead to Java EE 7》一文。Java EE 7 是以“日期驅動”的方式開發的,也就是說,在計劃日期到達前沒有完成的功能都將被推遲到 Java EE 8。

    Java EE 7(JSR-342)

    • 主題:讓應用程序能夠在私有或公共云上容易地運行。
    • 該平臺將定義一個應用程序元數據描述符,以描述 PaaS 執行環境(例如多租戶、資源共享、服務質量,以及應用程序間的依賴)。
    • 支持 HTML5、WebSocket、JSON 等新標準,并為它們一一提供 API。
    • 消除受管 Bean、EJB、Servlet、JSF、CDI 和 JAX-RS 之間不一致的地方。
    • 可能在 Web 配置文件中包含 JAX-RS 2.0 和 JMS 2.0 API 修訂版。
    • 更新一些現有的技術,可能引入用于 Java EE 的并發工具(JSR-236)和 JCache(JSR-107)。

    Java Persistence 2.1(JSR-338)

    • 支持多租戶。
    • 支持存儲過程和廠商函數。
    • 用規則(Criteria)進行更新和刪除。
    • 支持數據庫大綱(Scheme)的生成。
    • 持久化上下文的同步。
    • 偵聽器中的 CDI 注入。

    JAX-RS 2.0: The Java API for RESTful Web Services(JSR-339)

    • 客戶端 API——底層使用構建者模式,可能提供上層封裝。
    • 超媒體——輕松創建和處理關聯了資源的鏈接。
    • 使用 Bean 驗證框架來驗證表單或查詢參數。
    • @Inject 更緊密集成。
    • 服務端的異步請求處理。
    • 使用“qs”進行服務端的內容協商。

    Java Servlet 3.1(JSR-340)

    • 為 Web 應用程序優化 PaaS 模型。
    • 用于安全、會話和資源的多租戶。
    • 基于 NIO2 的異步 I/O。
    • 簡化的異步 Servlet。
    • 利用 Java EE 并發工具。
    • 支持 WebSocket。

    Expression Language 3.0(JSR-341)

    • ELContext 分離為解析和求值上下文。
    • 可定制的 EL 強迫規則。
    • 在 EL 表達式中直接引用靜態方法和成員。
    • 添加運算符,例如等于、字符串連接和取大小。
    • 與 CDI 集成,例如在表達式求值前/中/后生成事件。

    Java Message Service 2.0(JSR-343)

    • 簡化開發——改變 JMS 編程模型,讓應用程序開發變得更加簡單容易。
    • 清除/澄清現有規范中的模糊之處。
    • 與 CDI 集成。
    • 澄清 JMS 和其他 Java EE 規范之間的關系。
    • 新的強制性 API允許任何 JMS 提供者能與任何 Java EE 容器集成。
    • 來自平臺的多租戶和其他云相關的功能。

    JavaServer Faces 2.2(JSR-344)

    • 簡化開發——使配置選項動態化,使復合組件中的 cc:interface 可選,Facelet 標記庫的速記 URL,與 CDI 集成,JSF 組件的 OSGi 支持。
    • 支持 Portlet 2.0 橋(JSR-329)的實現。
    • 支持 HTML5 的功能,例如 HTML5 表單、元數據、頭部和區段內容模型。
    • 流管理,頁面導航事件的偵聽器,以及 fileUploadBackButton 等新組件。

    Enterprise JavaBeans 3.2(JSR-345)

    • 增強 EJB 架構以支持 PaaS,例如多租戶。
    • 對在 EJB 外使用容器管理的事務進行工廠化。
    • 更進一步使用注解。
    • 與平臺中的其他規范對齊和集成。

    Contexts and Dependency Injection 1.1(JSR-346)

    • 攔截器的全局排序和管理內建上下文的裝飾器 API。
    • 可在 Java EE 容器外啟動的嵌入式模式。
    • 聲明式地控制歸檔中的哪些包和 Bean 將被掃描。
    • 注入日志之類的靜態成員。
    • 將 Servlet 事件作為 CDI 事件發送。

    Bean Validation 1.1(JSR-349)

    • 與其他 Java EE 規范集成。
    • JAX-RS:在 HTTP 調用中驗證參數和返回值。
    • JAXB:將約束條件轉換到 XML 模式描述符中。
    • 方法級別的驗證。
    • 在組集合上應用約束條件。
    • 擴展模型以支持“與”和“或”風格的組合。

    JCache: Java Temporary Caching API(JSR-107)

    • 在內存中暫存 Java 對象的 API 和語義,包括對象的創建、共享訪問、緩存池、失效,以及跨 JVM 的一致性。

    Java State Management(JSR-350)

    • 應用程序和 Java EE 容器可使用該 API 將狀態管理的任務交給具有不同 QoS 特征的第三方提供者。
    • 基于 Java SE 的調用者可通過查詢狀態提供者來訪問狀態數據。
    • 可添加具有不同 QoS 的提供者,API 調用者能夠按自己的規則進行查詢。

    Batch Applications for the Java Platform(JSR-352)

    • 用于批處理應用程序的編程模型,以及用于調度和執行工作的運行時。
    • 為標準編程模型定義批處理工作、批處理工作步驟、批處理應用程序、批處理執行器和批處理工作管理器。

    Concurrency Utilities for Java EE(JSR-236)

    • 提供一個整潔、簡單且獨立的 API,使其能用于任何 Java EE 容器中。

    Java API for JSON Processing(JSR-353)

    • 處理 JSON 的 Java API。

    posted @ 2012-02-13 22:23 蜀山兆孨龘 閱讀(5757) | 評論 (0)編輯 收藏

    2012年2月9日

    ForkJoinPool 是 Java SE 7 新功能“分叉/結合框架”的核心類,現在可能乏人問津,但我覺得它遲早會成為主流。分叉/結合框架是一個比較特殊的線程池框架,專用于需要將一個任務不斷分解成子任務(分叉),再不斷進行匯總得到最終結果(結合)的計算過程。比起傳統的線程池類 ThreadPoolExecutorForkJoinPool 實現了工作竊取算法,使得空閑線程能夠主動分擔從別的線程分解出來的子任務,從而讓所有的線程都盡可能處于飽滿的工作狀態,提高執行效率。

    ForkJoinPool 提供了三類方法來調度子任務:

    execute 系列
    異步執行指定的任務。
    invokeinvokeAll
    執行指定的任務,等待完成,返回結果。
    submit 系列
    異步執行指定的任務并立即返回一個 Future 對象。

    子任務由 ForkJoinTask 的實例來代表。它是一個抽象類,JDK 為我們提供了兩個實現:RecursiveTaskRecursiveAction,分別用于需要和不需要返回計算結果的子任務。ForkJoinTask 提供了三個靜態的 invokeAll 方法來調度子任務,注意只能在 ForkJoinPool 執行計算的過程中調用它們。

    ForkJoinPoolForkJoinTask 還提供了很多讓人眼花繚亂的公共方法,其實它們大多數都是其內部實現去調用的,對于應用開發人員來說意義不大。

    下面以統計 D 盤文件個數為例。這實際上是對一個文件樹的遍歷,我們需要遞歸地統計每個目錄下的文件數量,最后匯總,非常適合用分叉/結合框架來處理:

    // 處理單個目錄的任務
    public class CountingTask extends RecursiveTask<Integer> {
        private Path dir;
    
        public CountingTask(Path dir) {
            this.dir = dir;
        }
    
        @Override
        protected Integer compute() {
            int count = 0;
            List<CountingTask> subTasks = new ArrayList<>();
    
            // 讀取目錄 dir 的子路徑。
            try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
                for (Path subPath : ds) {
                    if (Files.isDirectory(subPath, LinkOption.NOFOLLOW_LINKS)) {
                        // 對每個子目錄都新建一個子任務。
                        subTasks.add(new CountingTask(subPath));
                    } else {
                        // 遇到文件,則計數器增加 1。
                        count++;
                    }
                }
    
                if (!subTasks.isEmpty()) {
                    // 在當前的 ForkJoinPool 上調度所有的子任務。
                    for (CountingTask subTask : invokeAll(subTasks)) {
                        count += subTask.join();
                    }
                }
            } catch (IOException ex) {
                return 0;
            }
            return count;
        }
    }
    
    // 用一個 ForkJoinPool 實例調度“總任務”,然后敬請期待結果……
    Integer count = new ForkJoinPool().invoke(new CountingTask(Paths.get("D:/")));
        

    在我的筆記本上,經多次運行這段代碼,耗費的時間穩定在 600 豪秒左右。普通線程池(Executors.newCachedThreadPool())耗時 1100 毫秒左右,足見工作竊取的優勢。

    結束本文前,我們來圍觀一個最神奇的結果:單線程算法(使用 Files.walkFileTree(...))比這兩個都快,平均耗時 550 毫秒!這警告我們并非引入多線程就能優化性能,并須要先經過多次測試才能下結論。

    posted @ 2012-02-09 10:40 蜀山兆孨龘 閱讀(2681) | 評論 (2)編輯 收藏

    2012年1月19日

    前面已經看到,Socket 類的 getInputStream()getOutStream() 方法分別獲取套接字的輸入流和輸出流。輸入流用來讀取遠端發送過來的數據,輸出流則用來向遠端發送數據。

    輸入流

    使用套接字的輸入流讀取數據時,當前線程會進入阻塞狀態,直到套接字收到一些數據為止(亦即套接字的接收緩沖區有可用數據)。該輸入流的 available() 方法只是返回接收緩沖區的可用字節數量,不可能知道遠端還要發送多少字節。使用輸入流的時候,最好先將它包裝為一個 BufferedInputStream,因為讀取接收緩沖區將導致 JVM 和底層系統之間的切換,應當盡量減少切換次數以提高性能。BufferedInputStream 的緩沖區大小最好設為套接字接收緩沖區的大小。

    如果直接調用輸入流的 close() 方法來關閉它,則將導致套接字被關閉。對此,Socket 類提供了一個 shutdownInput() 方法來禁用輸入流。調用該方法后,每次讀操作都將返回 EOF,無法再讀取遠端發送的數據。對這個 EOF 的檢測,不同的輸入流包裝體現出不同的結果,可能讀到 -1 個字節,可能讀到的字符串為 null,還可能收到一個 EOFException 等等。禁用輸入流后,遠端輸出流的行為是平臺相關的:

    • 在 BSD 平臺上,遠端的發送的數據能正常接收,然后直接丟棄。遠端無法知道本端的輸入流已禁用。這和 JDK 文檔描述的行為一致。
    • 在 WINSOCK 平臺上,遠端發送數據將會導致“連接被重置”的錯誤。
    • 在 Linux 平臺上,遠端發送的數據能繼續接收,直到套接字輸入緩沖區填滿,之后遠端再也無法發送數據(若使用阻塞模式則進入死鎖)。

    禁用輸入流這種技術并不常用。

    輸出流

    套接字的輸出操作實際上僅僅將數據寫到發送緩沖區內,當發送緩沖區填滿且上次的發送成功后,由底層系統負責發送。如果發送緩沖區的剩余空間不夠,當前線程就會阻塞。和輸入流類似,最好將輸出流包裝為 BufferedOutputStream

    如果套接字的雙發都使用 ObjectInputStreamObjectOutputStream 來讀寫 Java 對象,則必須先創建 ObjectOutputStream,因為 ObjectInputStream 在構造的時候會試圖讀取對象頭部,如果雙發都先創建 ObjectInputStream,則會互相等待對方的輸出,造成死鎖:

    // 創建的順序不能顛倒!
    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
        

    類似于輸入流,關閉輸出流也導致關閉套接字,所以 Socket 類同樣提供了一個 shutdownOutput() 來禁用輸出流。禁用輸出流后,已寫入發送緩沖區的數據會正常發送,之后的任何寫操作都會導致 IOException,且遠端的輸入流始終會讀到 EOF。禁用輸出流非常有用,例如套接字的雙發都在發送完畢數據后禁用輸入流,然后雙方都會收到 EOF,從而知道數據已經全部交換完畢,可以安全關閉套接字。直接關閉套接字會同時關閉輸入流和輸出流,且斷開連接,達不到這種效果。

    使用流的阻塞套接字的優缺點

    如果要使用流進行輸入和輸出,就只能用阻塞模式的套接字。這里總結一下阻塞套接字的優缺點。先看看優點:

    1. 編程模型簡單,非常適合初學者上手。
    2. 以裝飾器模式設計的 Java I/O 使得開發人員可以輕松地從 I/O 流讀寫任何類型的數據。

    但在性能方面有致命的缺點:

    1. 由于服務器套接字接受連接,以及套接字的讀寫都會阻塞,性能低下。
    2. 如果不對 I/O 流手動進行緩沖,則可能造成一次只處理一個字節,性能低下。
    3. 服務器套接字每次只能接受一個連接,導致 JVM 和底層系統之間頻繁的調用切換,性能低下。

    下一篇文章開始探討使用基于 NIO 的套接字通道和緩沖區實現伸縮性更強的 TCP 套接字。

    posted @ 2012-01-19 14:37 蜀山兆孨龘 閱讀(2276) | 評論 (1)編輯 收藏

    2012年1月16日

    ServerSocket 類和 Socket 類都提供了多個公共構造方法。不同的構造方法不僅帶的參數不同,所具有的意義也不一樣。下面分別解析這兩個類的實例初始化過程。

    ServerSocket 實例的初始化

    ServerSocket 類提供了四個構造器:

    public ServerSocket(int port) throws IOException
    public ServerSocket(int port, int backlog) throws IOException
    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
    public ServerSocket() throws IOException

    帶參構造器用來創建已綁定的服務器套接字,也就是說構造成功后它就已經開始偵聽指定的端口,且能夠調用 accept() 方法來接受客戶端連接。默認構造器則會創建未綁定的服務器套接字,構造成功后必須手動將其綁定到一個本地地址才能用,在綁定之前可以進行一些選項配置。

    帶參構造器

    總的來說,帶參構造器提供了三個參數:

    port
    指定該服務器套接字所要偵聽的本地端口。如果為 0,則由系統自動分配一個端口號,這必須以另外的方式讓客戶端獲取端口號。
    backlog
    這個名詞目前還沒有合適的譯名。底層系統的 TCP 實現會維護一個連接隊列,該隊列緩存了已被 TCP 處理完畢,但還沒有被服務器套接字接受的客戶端連接。一旦某個連接被接受(通過調用 accept() 方法),它就會被從隊列中移除。backlog 參數就用于指定隊列的最大長度,默認值為 50,但這個值只是一個建議,底層系統可能根據需要自動調整。如果隊列滿了,則其行為是平臺相關的:微軟的 WINSOCK 會拒絕新的連接,其他實現則什么都不做。嚴格地說,微軟沒有遵守規范,破壞了游戲規則……
    bindAddr
    一臺機器可能會有多個本地 IP 地址,例如同時使用多塊網卡。使用其他兩個帶參構造器時,該參數為 null,服務器套接字會在所有的本地 IP 地址(0.0.0.0::0)上偵聽。如果希望只偵聽一個地址,則可使用該參數。

    默認構造器

    如果使用默認構造器,在綁定地址前,還可以做些配置。綁定操作由兩個 bind 方法定義,參數類似于帶參構造器。配置項包括以下方面(都必須在綁定前配置):

    設置是否重用本地地址
    該選項由 setReuseAddress(boolean on) 方法配置,對應底層系統的 SO_REUSEADDR 套接字選項。JDK 沒有定義該選項的默認值。如果該選項為 false,則在關閉 TCP 連接時,為了保證可靠性,該連接可能在關閉后的一段時間(大約兩分鐘)內保持超時狀態(通常稱為 TIME_WAIT 狀態或 2MSL 等待狀態),這段時間里無法將新建的服務器套接字綁定到同一個地址。在開發階段,服務器可能不斷重啟,打開改選項會非常有用。
    設置接收緩沖區大小
    該選項由 setReceiveBufferSize(int size) 方法配置,對應底層系統的 SO_RCVBUF 套接字選項,單位是字節。《RFC 1323 - TCP Extensions for High Performance》將緩沖區大小定義為 64KB。該選項只是一個建議值,底層系統可能根據需要自行調整。
    設置超時值
    該選項由 setSoTimeout(int timeout) 方法配置,對應底層系統的 SO_TIMEOUT 套接字選項,單位是毫秒。默認值為 0。該選項影響 accept 方法的阻塞時間長度,如果超時將引發 SocketTimeoutException。如果設為 0,則表示永不超時。
    設置性能首選項
    性能首選項包括連接時間、延遲和帶寬三個選項,由 setPerformancePreferences(int connectionTime, int latency, int bandwidth) 方法配置。這三個數值分別表示短連接時間、低延遲和高帶寬的相對重要性,數值越大則越重要;其各自的絕對值沒有意義。該方法的初衷是為了讓 Java 能在用非 TCP/IP 實現的套接字環境下工作得更好,某些需要對網絡進行調優的程序也可以將這三個首選項作為配置參數提供給用戶。

    Socket 實例的初始化

    Socket 類提供了六個公共構造器(已過時的除外):

    public Socket(String host, int port) throws UnknownHostException, IOException
    public Socket(InetAddress address, int port) throws IOException
    public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
    public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
    public Socket()
    public Socket(Proxy proxy)

    前四個構造器創建已連接的客戶端套接字,也就是說構造的時候就會去連接服務器。前兩個構造器需要提供服務器的地址和端口作為參數,本地地址和端口由系統自動分配;后兩個允許手動指定本地地址和端口,但極少使用。后兩個構造器創建未連接的套接字,創建后需要調用 connect 方法手動連接,連接之前可以做一些配置。最后一個構造器接受一個代表代理服務其的 Proxy 對象,JDK 支持 HTTP 和 SOCKS(V4 或 V5)兩種代理類型。

    連接前的配置

    在連接前,客戶端套接字不僅像服務器套接字那樣可以設置是否重用本地地址、緩沖區大小、超時值和性能首選項,還能夠配置以下各項(都必須在連接前配置):

    設置是否保持活躍
    該選項由 setKeepAlive(boolean on) 方法配置,對應底層系統的 SO_KEEPALIVE 套接字選項。默認值為 false。如果打開該選項,則套接字會定期自動發送保持活躍的探測性消息,類似于心跳檢測。根據《RFC 1122 - Requirements for Internet Hosts》的規定,保持活躍機制只是 TCP 的一個可選功能,如果支持的話,默認必須為 false,而且這種機制默認在成功建立連接后,且連續兩小時沒有數據傳輸的情況下才會被激活。從另一方面來看,通過套接字的 I/O 操作完全可以知道連接是否還有效,所以該選項的實用價值不大。
    設置是否收發帶外數據
    該選項由 setOOBInline(boolean on) 方法配置,對應底層系統的 SO_OOBINLINE 套接字選項。默認值為 off。帶外數據(Out-of-band Data)也叫做緊急數據,表示數據很重要,需要使用不同于發送普通數據的一個專用通道來發送。打開該選項后,就可以調用 sendUrgentData(int data) 方法發送一個字節的緊急數據。JDK 對帶外數據只提供了有限支持,緊急數據將會和普通數據一起被收到,并且無法自動區分。該選項對應用開發人員意義不大。
    設置是否從容關閉連接
    該選項由 setSoLinger(boolean on, int linger) 方法配置,對應底層系統的 SO_LINGER 套接字選項。默認為 false。該選項只會影響套接字的關閉,其中的 linger 參數表示超時時間,單位為秒。如果打開改選項:如果將 linger 設為 0,則關閉套接字的時候,未發送的數據會被丟棄,且另一端會出現連接被同位體重置的異常;如果 linger 非 0,則關閉套接字的線程將被阻塞,直到數據全部發送或超時,超時后的行為與底層系統相關,JDK 無法控制。如果關閉該選項,則套接字正常關閉,數據也會全部發送。由于底層實現的差異性,不提倡應用開發人員打開該選項。
    設置是否延遲發送數據
    該選項由 setTcpNoDelay(boolean on) 方法配置,對應底層系統的 TCP_NODELAY TCP 選項。默認值為 off。打開該選項將禁用 Nagle 算法,TCP 包會立即發送;關閉該選項則會啟用 Nagle 算法,多個較小的 TCP 包會被組合成一個大包一起發送,雖然發送延遲了,但有利于避免網絡擁塞。默認為 false。該選項對實時性很強的程序可能有用,但一般的程序不需要關心。
    設置流量類別
    該選項由 setTrafficClass(int tc) 方法配置,對應底層系統的“流量類別”套接字屬性。該選項用于向網絡(例如路由器)提示從該套接字發送的包需要獲取哪些服務類型,對本地 TCP 協議棧沒有影響。IPv4 和 IPv6 分別定義了多個不同的值,例如 IPv4 將 0x08 定義為最大吞吐量,0x10 定義為最小延遲,等等。可以用或運算將多個值合并為一個選項。該選項用來調整性能,需要根據實際情況設置。由于只是建議值,可能被網絡忽略。

    posted @ 2012-01-16 10:31 蜀山兆孨龘 閱讀(2300) | 評論 (2)編輯 收藏

    2012年1月10日

    網上很多關于單例模式寫法的文章,不外乎餓漢和懶漢兩種形式的討論。很多人喜歡用懶漢式,因為覺得它實現了延遲加載,可以讓系統的性能更好。但事實果真如此嗎?我對此存疑。

    首先我們檢查一下餓漢和懶漢單例模式最簡單的寫法(這里不討論哪種懶漢寫法更好):

    // 餓漢
    public final class HungrySingleton {
        private static final HungrySingleton INSTANCE = new HungrySingleton();
    
        private HungrySingleton() {
            System.out.println("Initializing...");
        }
    
        public static HungrySingleton getInstance() {
            return INSTANCE;
        }
    }
    
    // 懶漢
    public final class LazySingleton {
        private static LazySingleton INSTANCE;
    
        private LazySingleton() {
            System.out.println("Initializing...");
        }
    
        public static synchronized LazySingleton getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new LazySingleton();
            }
            return INSTANCE;
        }
    }
        

    從理論上來說,HungrySingleton 的單例在該類第一次使用的時候創建,而 LazySingleton 的單例則在其 getInstance() 方法被調用的時候創建。至于網上有人聲稱“餓漢式不管用不用都會初始化”,純屬走路的時候步子邁得太大。誰的加載更遲?如果你只是調用它們的 getInstance() 方法來得到單例對象,則它們都是延遲加載,這樣懶漢式沒有任何意義,而且由于 LazySingleton 采取了同步措施,性能更低(可以說任何懶漢式的性能都低于餓漢式)。當你使用一個單例類的時候,難道第一步不是調用 getInstance() 么?所以在自己的代碼里,我更喜歡用餓漢式。

    下面用一個例子來測試加載順序:

    // 餓漢
    System.out.println("Before");
    HungrySingleton.getInstance();
    System.out.println("After");
    
    // 懶漢
    System.out.println("Before");
    LazySingleton.getInstance();
    System.out.println("After");
        

    輸出結果都是:

    Before
    Initializing...
    After

    那么,懶漢模式還有什么存在意義?如果系統使用了某些需要在啟動時對類進行掃描的框架,使用餓漢式的話,啟動時間比懶漢式更長,如果使用了大量單例類,不利于開發階段。在系統的正式運行階段,所有的單例類遲早都要加載的,總的說來兩者性能持平,但是懶漢式每次都至少多一個判斷,所以越到后期越體現餓漢的優越性。

    最后,推薦下《Effective Java》第二版指出的用枚舉類型實現的餓漢單例模式:

    // 餓漢
    public enum HungrySingleton {
        INSTANCE;
    
        private HungrySingleton() {
        }
    }
    

    這種寫法不但最簡潔,還能輕易擴展為實例數量固定的“多例模式”。

    posted @ 2012-01-10 17:39 蜀山兆孨龘 閱讀(1847) | 評論 (6)編輯 收藏

    主站蜘蛛池模板: 男女啪啪免费体验区| 24小时免费看片| 免费毛片在线看不用播放器| 在线观看www日本免费网站| 亚洲国产成人高清在线观看| 亚洲三级在线观看| 中文日本免费高清| 丁香花在线观看免费观看| 国产精品亚洲高清一区二区| 亚洲国产视频久久| 18禁超污无遮挡无码免费网站国产 | 久久久www成人免费毛片| 亚洲麻豆精品果冻传媒| 亚洲AV无码AV男人的天堂不卡| 国产免费一级高清淫曰本片| 嫩草视频在线免费观看| 亚洲成人午夜在线| 一级毛片高清免费播放| 色www永久免费视频| 亚洲欧洲尹人香蕉综合| 久久久国产精品福利免费| 天堂亚洲免费视频| 国产亚洲福利在线视频| 国产精品深夜福利免费观看| 亚洲人精品亚洲人成在线| 波多野结衣久久高清免费 | 亚洲香蕉成人AV网站在线观看| 国产v亚洲v天堂a无| 91嫩草免费国产永久入口| 亚洲成?Ⅴ人在线观看无码| 亚洲欧美日韩中文无线码 | 亚洲欧美日韩久久精品| 国产福利电影一区二区三区,亚洲国模精品一区| yy一级毛片免费视频| 亚洲黄色三级网站| 亚洲?v女人的天堂在线观看| 久操免费在线观看| 久久亚洲国产精品五月天婷| 免费夜色污私人影院网站| 久久久久亚洲精品美女| 色吊丝最新永久免费观看网站|