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

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

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

    少年阿賓

    那些青春的歲月

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

    #

    關(guān)于springmvc的幾個(gè)注解,基本都在org.springframework:spring-webmvc:3.1.4.REALEASE這個(gè)包里面處理的,具體是在org.springframework.web.servlet.mvc.annotation這個(gè)包里面的AnnotationMethodHandlerAdapter這個(gè)類里面處理的。
    posted @ 2015-03-23 21:10 abin 閱讀(378) | 評(píng)論 (0)編輯 收藏

    1、使用<context:annotation-config />簡化配置 
          Spring2.1添加了一個(gè)新的context的Schema命名空間,該命名空間對注釋驅(qū)動(dòng)、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道注釋本身是不會(huì)做任何事情的,它僅提供元數(shù)據(jù)信息。要使元數(shù)據(jù)信息真正起作用,必須讓負(fù)責(zé)處理這些元數(shù)據(jù)的處理器工作起來。 
          AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor就是處理這些注釋元數(shù)據(jù)的處理器。但是直接在Spring配置文件中定義這些Bean顯得比較笨拙。Spring為我們提供了一種方便的注冊這些BeanPostProcessor的方式,這就是<context:annotation-config />:
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
        <context:annotation-config />  
    </beans>  

    <context:annotationconfig />將隱式地向Spring容器注冊AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor以及RequiredAnnotationBeanPostProcessor這4個(gè)BeanPostProcessor。

    2、使用<context:component-scan />讓Bean定義注解工作起來
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
        <context:component-scan base-package="com.kedacom.ksoa" />  
    </beans>  

    這里,所有通過<bean>元素定義Bean的配置內(nèi)容已經(jīng)被移除,僅需要添加一行<context:component-scan />配置就解決所有問題了——Spring XML配置文件得到了極致的簡化(當(dāng)然配置元數(shù)據(jù)還是需要的,只不過以注釋形式存在罷了)。<context:component-scan />的base-package屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會(huì)被處理。 
    < context:component-scan />還允許定義過濾器將基包下的某些類納入或排除。Spring支持以下4種類型的過濾方式: 
    過濾器類型 表達(dá)式范例 說明
    注解 org.example.SomeAnnotation將所有使用SomeAnnotation注解的類過濾出來
    類名指定 org.example.SomeClass過濾指定的類
    正則表達(dá)式 com\.kedacom\.spring\.annotation\.web\..*通過正則表達(dá)式過濾一些類
    AspectJ表達(dá)式 org.example..*Service+通過AspectJ表達(dá)式過濾一些類


    以正則表達(dá)式為例,我列舉一個(gè)應(yīng)用實(shí)例:
    <context:component-scan base-package="com.casheen.spring.annotation">  
        <context:exclude-filter type="regex" expression="com\.casheen\.spring\.annotation\.web\..*" />  
    </context:component-scan>  

    值得注意的是<context:component-scan />配置項(xiàng)不但啟用了對類包進(jìn)行掃描以實(shí)施注釋驅(qū)動(dòng)Bean定義的功能,同時(shí)還啟用了注釋驅(qū)動(dòng)自動(dòng)注入的功能(即還隱式地在內(nèi)部注冊了AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor),因此當(dāng)使用<context:component-scan />后,就可以將<context:annotation-config />移除了.

    posted @ 2015-03-23 19:38 abin 閱讀(413) | 評(píng)論 (0)編輯 收藏

    Java 虛擬機(jī)(JVM)是可運(yùn)行Java 代碼的假想計(jì)算機(jī)。只要根據(jù)JVM規(guī)格描述將解釋器移植到特定的計(jì)算機(jī)上,就能保證經(jīng)過編譯的任何Java代碼能夠在該系統(tǒng)上運(yùn)行。本文首先簡要介紹從Java文件的編譯到最終執(zhí)行的過程,隨后對JVM規(guī)格描述作一說明。
      
      一.Java源文件的編譯、下載 、解釋和執(zhí)行 
      Java應(yīng)用程序的開發(fā)周期包括編譯、下載 、解釋和執(zhí)行幾個(gè)部分。Java編譯程序?qū)ava源程序翻譯為JVM可執(zhí)行代碼?字節(jié)碼。這一編譯過程同C/C++ 的 編譯有些不同。當(dāng)C編譯器編譯生成一個(gè)對象的代碼時(shí),該代碼是為在某一特定硬件平臺(tái)運(yùn)行而產(chǎn)生的。因此,在編譯過程中,編譯程序通過查表將所有對符號(hào)的引 用轉(zhuǎn)換為特定的內(nèi)存偏移量,以保證程序運(yùn)行。Java編譯器卻不將對變量和方法的引用編譯為數(shù)值引用,也不確定程序執(zhí)行過程中的內(nèi)存布局,而是將這些符號(hào) 引用信息保留在字節(jié)碼中,由解釋器在運(yùn)行過程中創(chuàng)立內(nèi)存布局,然后再通過查表來確定一個(gè)方法所在的地址。這樣就有效的保證了Java的可移植性和安全 性。
      
       運(yùn)行JVM字節(jié)碼的工作是由解釋器來完成的。解釋執(zhí)行過程分三部進(jìn)行:代碼的裝入、代碼的校驗(yàn)和代碼的執(zhí)行。裝入代碼的工作由"類裝載器"(class loader)完成。類裝載器負(fù)責(zé)裝入運(yùn)行一個(gè)程序需要的所有代碼,這也包括程序代碼中的類所繼承的類和被其調(diào)用的類。當(dāng)類裝載器裝入一個(gè)類時(shí),該類被放 在自己的名字空間中。除了通過符號(hào)引用自己名字空間以外的類,類之間沒有其他辦法可以影響其他類。在本臺(tái)計(jì)算機(jī)上的所有類都在同一地址空間內(nèi),而所有從外 部引進(jìn)的類,都有一個(gè)自己獨(dú)立的名字空間。這使得本地類通過共享相同的名字空間獲得較高的運(yùn)行效率,同時(shí)又保證它們與從外部引進(jìn)的類不會(huì)相互影響。當(dāng)裝入 了運(yùn)行程序需要的所有類后,解釋器便可確定整個(gè)可執(zhí)行程序的內(nèi)存布局。解釋器為符號(hào)引用同特定的地址空間建立對應(yīng)關(guān)系及查詢表。通過在這一階段確定代碼的 內(nèi)存布局,Java很好地解決了由超類改變而使子類崩潰的問題,同時(shí)也防止了代碼對地址的非法訪問。
      
      隨后,被裝入的代碼由字節(jié)碼校驗(yàn)器進(jìn)行檢查。校驗(yàn)器可發(fā)現(xiàn)操作數(shù)棧溢出,非法數(shù)據(jù)類型轉(zhuǎn)化等多種錯(cuò)誤。通過校驗(yàn)后,代碼便開始執(zhí)行了。
      
      Java字節(jié)碼的執(zhí)行有兩種方式:
      1.即時(shí)編譯方式:解釋器先將字節(jié)碼編譯成機(jī)器碼,然后再執(zhí)行該機(jī)器碼。
      2.解釋執(zhí)行方式:解釋器通過每次解釋并執(zhí)行一小段代碼來完成Java字節(jié)碼程 序的所有操作。
      通常采用的是第二種方法。由于JVM規(guī)格描述具有足夠的靈活性,這使得將字節(jié)碼翻譯為機(jī)器代碼的工作
      
      具有較高的效率。對于那些對運(yùn)行速度要求較高的應(yīng)用程序,解釋器可將Java字節(jié)碼即時(shí)編譯為機(jī)器碼,從而很好地保證了Java代碼的可移植性和高性能。
      
      二.JVM規(guī)格描述 
       JVM的設(shè)計(jì)目標(biāo)是提供一個(gè)基于抽象規(guī)格描述的計(jì)算機(jī)模型,為解釋程序開發(fā)人員提很好的靈活性,同時(shí)也確保Java代碼可在符合該規(guī)范的任何系統(tǒng)上運(yùn) 行。JVM對其實(shí)現(xiàn)的某些方面給出了具體的定義,特別是對Java可執(zhí)行代碼,即字節(jié)碼(Bytecode)的格式給出了明確的規(guī)格。這一規(guī)格包括操作碼 和操作數(shù)的語法和數(shù)值、標(biāo)識(shí)符的數(shù)值表示方式、以及Java類文件中的Java對象、常量緩沖池在JVM的存儲(chǔ) 映象。這些定義為JVM解釋器開發(fā)人員提供了所需的信息和開發(fā)環(huán)境。Java的設(shè)計(jì)者希望給開發(fā)人員以隨心所欲使用Java的自由。
      
      JVM定義了控制Java代碼解釋執(zhí)行和具體實(shí)現(xiàn)的五種規(guī)格,它們是:
      JVM指令系統(tǒng)
      JVM寄存器
      JVM棧結(jié)構(gòu)
      JVM碎片回收堆
      JVM存儲(chǔ) 區(qū)
      
      2.1JVM指令系統(tǒng)
      
       JVM指令系統(tǒng)同其他計(jì)算機(jī)的指令系統(tǒng)極其相似。Java指令也是由 操作碼和操作數(shù)兩部分組成。操作碼為8位二進(jìn)制數(shù),操作數(shù)進(jìn)緊隨在操作碼的后面,其長度根據(jù)需要而不同。操作碼用于指定一條指令操作的性質(zhì)(在這里我們采 用匯編符號(hào)的形式進(jìn)行說明),如iload表示從存儲(chǔ)器中裝入一個(gè)整數(shù),anewarray表示為一個(gè)新數(shù)組分配空間,iand表示兩個(gè)整數(shù)的" 與",ret用于流程控制,表示從對某一方法的調(diào)用中返回。當(dāng)長度大于8位時(shí),操作數(shù)被分為兩個(gè)以上字節(jié)存放。JVM采用了"big endian"的編碼方式來處理這種情況,即高位bits存放在低字節(jié)中。這同 Motorola及其他的RISC CPU采用的編碼方式是一致的,而與Intel采用的"little endian "的編碼方式即低位bits存放在低位字節(jié)的方法不同。
      
      Java指令系統(tǒng)是以Java語言的實(shí)現(xiàn)為目的設(shè)計(jì)的,其中包含了用于調(diào)用方法和監(jiān)視多先程系統(tǒng)的指令。Java的8位操作碼的長度使得JVM最多有256種指令,目前已使用了160多種操作碼。
      
      2.2JVM指令系統(tǒng)
      
       所有的CPU均包含用于保存系統(tǒng)狀態(tài)和處理器所需信息的寄存器組。如果虛擬機(jī)定義較多的寄存器,便可以從中得到更多的信息而不必對棧或內(nèi)存進(jìn)行訪問,這 有利于提高運(yùn)行速度。然而,如果虛擬機(jī)中的寄存器比實(shí)際CPU的寄存器多,在實(shí)現(xiàn)虛擬機(jī)時(shí)就會(huì)占用處理器大量的時(shí)間來用常規(guī)存儲(chǔ)器模擬寄存器,這反而會(huì)降 低虛擬機(jī)的效率。針對這種情況,JVM只設(shè)置了4個(gè)最為常用的寄存器。它們是:
      pc程序計(jì)數(shù)器
      optop操作數(shù)棧頂指針
      frame當(dāng)前執(zhí)行環(huán)境指針
      vars指向當(dāng)前執(zhí)行環(huán)境中第一個(gè)局部變量的指針
      所有寄存器均為32位。pc用于記錄程序的執(zhí)行。optop,frame和vars用于記錄指向Java棧區(qū)的指針。
      
      2.3JVM棧結(jié)構(gòu)
      
      作為基于棧結(jié)構(gòu)的計(jì)算機(jī),Java棧是JVM存儲(chǔ)信息的主要方法。當(dāng)JVM得到一個(gè)Java字節(jié)碼應(yīng)用程序后,便為該代碼中一個(gè)類的每一個(gè)方法創(chuàng)建一個(gè)棧框架,以保存該方法的狀態(tài)信息。每個(gè)棧框架包括以下三類信息:
      局部變量
      執(zhí)行環(huán)境
      操作數(shù)棧
      
      局部變量用于存儲(chǔ)一個(gè)類的方法中所用到的局部變量。vars寄存器指向該變量表中的第一個(gè)局部變量。
       執(zhí)行環(huán)境用于保存解釋器對Java字節(jié)碼進(jìn)行解釋過程中所需的信息。它們是:上次調(diào)用的方法、局部變量指針和操作數(shù)棧的棧頂和棧底指針。執(zhí)行環(huán)境是一個(gè) 執(zhí)行一個(gè)方法的控制中心。例如:如果解釋器要執(zhí)行iadd(整數(shù)加法),首先要從frame寄存器中找到當(dāng)前執(zhí)行環(huán)境,而后便從執(zhí)行環(huán)境中找到操作數(shù)棧, 從棧頂彈出兩個(gè)整數(shù)進(jìn)行加法運(yùn)算,最后將結(jié)果壓入棧頂。
      操作數(shù)棧用于存儲(chǔ)運(yùn)算所需操作數(shù)及運(yùn)算的結(jié)果。
      
      2.4JVM碎片回收堆
      
      Java類的實(shí)例所需的存儲(chǔ)空間是在堆上分配的。解釋器具體承擔(dān)為類實(shí)例分配空間的工作。解釋器在為一個(gè)實(shí)例分配完存儲(chǔ)空間后,便開始記錄對該實(shí)例所占用的內(nèi)存區(qū)域的使用。一旦對象使用完畢,便將其回收到堆中。
       在Java語言中,除了new語句外沒有其他方法為一對象申請和釋放內(nèi)存。對內(nèi)存進(jìn)行釋放和回收的工作是由Java運(yùn)行系統(tǒng)承擔(dān)的。這允許Java運(yùn)行 系統(tǒng)的設(shè)計(jì)者自己決定碎片回收的方法。在SUN公司開發(fā)的Java解釋器和Hot Java環(huán)境中,碎片回收用后臺(tái)線程的方式來執(zhí)行。這不但為運(yùn)行系統(tǒng)提供了良好的性能,而且使程序設(shè)計(jì)人員擺脫了自己控制內(nèi)存使用的風(fēng)險(xiǎn)。
      
      2.5JVM存儲(chǔ)區(qū)
      
       JVM有兩類存儲(chǔ)區(qū):常量緩沖池和方法區(qū)。常量緩沖池用于存儲(chǔ)類名稱、方法和字段名稱以及串常量。方法區(qū)則用于存儲(chǔ)Java方法的字節(jié)碼。對于這兩種存 儲(chǔ)區(qū)域具體實(shí)現(xiàn)方式在JVM規(guī)格中沒有明確規(guī)定。這使得Java應(yīng)用程序的存儲(chǔ)布局必須在運(yùn)行過程中確定,依賴于具體平臺(tái)的實(shí)現(xiàn)方式。
      
      JVM是為Java字節(jié)碼定義的一種獨(dú)立于具體平臺(tái)的規(guī)格描述,是Java平臺(tái)獨(dú)立性的基礎(chǔ)。目前的JVM還存在一些限制和不足,有待于進(jìn)一步的完善,但無論如何,JVM的思想是成功的。
      
      對比分析:如果把Java原程序想象成我們的C++ 原 程序,Java原程序編譯后生成的字節(jié)碼就相當(dāng)于C++原程序編譯后的80x86的機(jī)器碼(二進(jìn)制程序文件),JVM虛擬機(jī)相當(dāng)于80x86計(jì)算機(jī)系 統(tǒng),Java解釋器相當(dāng)于80x86CPU。在80x86CPU上運(yùn)行的是機(jī)器碼,在Java解釋器上運(yùn)行的是Java字節(jié)碼。
      
       Java解釋器相當(dāng)于運(yùn)行Java字節(jié)碼的“CPU”,但該“CPU”不是通過硬件實(shí)現(xiàn)的,而是用軟件實(shí)現(xiàn)的。Java解釋器實(shí)際上就是特定的平臺(tái)下的一 個(gè)應(yīng)用程序。只要實(shí)現(xiàn)了特定平臺(tái)下的解釋器程序,Java字節(jié)碼就能通過解釋器程序在該平臺(tái)下運(yùn)行,這是Java跨平臺(tái)的根本。當(dāng)前,并不是在所有的平臺(tái) 下都有相應(yīng)Java解釋器程序,這也是Java并不能在所有的平臺(tái)下都能運(yùn)行的原因,它只能在已實(shí)現(xiàn)了Java解釋器程序的平臺(tái)下運(yùn)行。


           Java主要靠Java虛擬機(jī)(JVM)在目標(biāo)碼級(jí)實(shí)現(xiàn)平臺(tái)無關(guān)性。JVM是一種抽象機(jī)器,它附著在具體操作系統(tǒng)之上,本身具有一套虛機(jī)器指令,并有自己的棧、寄存器組等。但JVM通常是在軟件上而不是在硬件上實(shí)現(xiàn)。(目前,SUN系統(tǒng)公司已經(jīng)設(shè)計(jì)實(shí)現(xiàn)了Java芯片,主要使用在網(wǎng)絡(luò)計(jì)算機(jī)NC上。另外,Java芯片的出現(xiàn)也會(huì)使Java更容易嵌入到家用電器中。)JVM是Java平臺(tái)無關(guān)的基礎(chǔ),在JVM上,有一個(gè)Java解釋器用來解釋Java編譯器編譯后的程序。Java編程人員在編寫完軟件后,通過Java編譯器將Java源程序編譯為JVM的字節(jié)代碼。任何一臺(tái)機(jī)器只要配備了Java解釋器,就可以運(yùn)行這個(gè)程序,而不管這種字節(jié)碼是在何種平臺(tái)上生成的(過程如圖1所示)。另外,Java采用的是基于IEEE標(biāo)準(zhǔn)的數(shù)據(jù)類型。通過JVM保證數(shù)據(jù)類型的一致性,也確保了Java的平臺(tái)無關(guān)性。 

    簡單說,java的解釋器只是一個(gè)基于虛擬機(jī)jvm平臺(tái)的程序 
    posted @ 2015-03-23 15:23 abin 閱讀(373) | 評(píng)論 (0)編輯 收藏

    由于JDK的安全檢查耗時(shí)較多.所以通過setAccessible(true)的方式關(guān)閉安全檢查就可以達(dá)到提升反射速度的目的 
    posted @ 2015-03-23 00:01 abin 閱讀(354) | 評(píng)論 (0)編輯 收藏

    在面向?qū)ο缶幊讨? 最通常的方法是一個(gè)new操作符產(chǎn)生一個(gè)對象實(shí)例,new操作符就是用來構(gòu)造對象實(shí)例的。但是在一些情況下, new操作符直接生成對象會(huì)帶來一些問題。舉例來說, 許多類型對象的創(chuàng)造需要一系列的步驟: 你可能需要計(jì)算或取得對象的初始設(shè)置; 選擇生成哪個(gè)子對象實(shí)例; 或在生成你需要的對象之前必須先生成一些輔助功能的對象。 在這些情況,新對象的建立就是一個(gè) “過程”,不僅是一個(gè)操作,像一部大機(jī)器中的一個(gè)齒輪傳動(dòng)。

    模式的問題:你如何能輕松方便地構(gòu)造對象實(shí)例,而不必關(guān)心構(gòu)造對象實(shí)例的細(xì)節(jié)和復(fù)雜過程呢?

    解決方案:建立一個(gè)工廠來創(chuàng)建對象

    實(shí)現(xiàn):

    一、引言
        1)還沒有工廠時(shí)代:假如還沒有工業(yè)革命,如果一個(gè)客戶要一款寶馬車,一般的做法是客戶去創(chuàng)建一款寶馬車,然后拿來用。
        2)簡單工廠模式:后來出現(xiàn)工業(yè)革命。用戶不用去創(chuàng)建寶馬車。因?yàn)榭蛻粲幸粋€(gè)工廠來幫他創(chuàng)建寶馬.想要什么車,這個(gè)工廠就可以建。比如想要320i系列車。工廠就創(chuàng)建這個(gè)系列的車。即工廠可以創(chuàng)建產(chǎn)品。
        3)工廠方法模式時(shí)代:為了滿足客戶,寶馬車系列越來越多,如320i,523i,30li等系列一個(gè)工廠無法創(chuàng)建所有的寶馬系列。于是由單獨(dú)分出來多個(gè)具體的工廠。每個(gè)具體工廠創(chuàng)建一種系列。即具體工廠類只能創(chuàng)建一個(gè)具體產(chǎn)品。但是寶馬工廠還是個(gè)抽象。你需要指定某個(gè)具體的工廠才能生產(chǎn)車出來。

       4)抽象工廠模式時(shí)代:隨著客戶的要求越來越高,寶馬車必須配置空調(diào)。于是這個(gè)工廠開始生產(chǎn)寶馬車和需要的空調(diào)。

       最終是客戶只要對寶馬的銷售員說:我要523i空調(diào)車,銷售員就直接給他523i空調(diào)車了。而不用自己去創(chuàng)建523i空調(diào)車寶馬車.

       這就是工廠模式。

    二、分類 
            工廠模式主要是為創(chuàng)建對象提供過渡接口,以便將創(chuàng)建對象的具體過程屏蔽隔離起來,達(dá)到提高靈活性的目的。 
    工廠模式可以分為三類: 

    1)簡單工廠模式(Simple Factory) 
    2)工廠方法模式(Factory Method) 
    3)抽象工廠模式(Abstract Factory) 

     這三種模式從上到下逐步抽象,并且更具一般性。 
            GOF在《設(shè)計(jì)模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。

            將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。 

    三、區(qū)別 
    工廠方法模式:
    一個(gè)抽象產(chǎn)品類,可以派生出多個(gè)具體產(chǎn)品類。   
    一個(gè)抽象工廠類,可以派生出多個(gè)具體工廠類。   
    每個(gè)具體工廠類只能創(chuàng)建一個(gè)具體產(chǎn)品類的實(shí)例。
    抽象工廠模式:
    多個(gè)抽象產(chǎn)品類,每個(gè)抽象產(chǎn)品類可以派生出多個(gè)具體產(chǎn)品類。   
    一個(gè)抽象工廠類,可以派生出多個(gè)具體工廠類。   
    每個(gè)具體工廠類可以創(chuàng)建多個(gè)具體產(chǎn)品類的實(shí)例。   
    區(qū)別:
    工廠方法模式只有一個(gè)抽象產(chǎn)品類,而抽象工廠模式有多個(gè)。   
    工廠方法模式的具體工廠類只能創(chuàng)建一個(gè)具體產(chǎn)品類的實(shí)例,而抽象工廠模式可以創(chuàng)建多個(gè)。
    兩者皆可。 



    http://blog.csdn.net/jason0539/article/details/23020989
    posted @ 2015-03-22 15:16 abin 閱讀(353) | 評(píng)論 (0)編輯 收藏

           重排序通常是編譯器或運(yùn)行時(shí)環(huán)境為了優(yōu)化程序性能而采取的對指令進(jìn)行重新排序執(zhí)行的一種手段。重排序分為兩類:編譯期重排序和運(yùn)行期重排序,分別對應(yīng)編譯時(shí)和運(yùn)行時(shí)環(huán)境

    在并發(fā)程序中,程序員會(huì)特別關(guān)注不同進(jìn)程或線程之間的數(shù)據(jù)同步,特別是多個(gè)線程同時(shí)修改同一變量時(shí),必須采取可靠的同步或其它措施保障數(shù)據(jù)被正確地修改,這里的一條重要原則是:不要假設(shè)指令執(zhí)行的順序,你無法預(yù)知不同線程之間的指令會(huì)以何種順序執(zhí)行。

    但是在單線程程序中,通常我們?nèi)菀准僭O(shè)指令是順序執(zhí)行的,否則可以想象程序會(huì)發(fā)生什么可怕的變化。理想的模型是:各種指令執(zhí)行的順序是唯一且有序的,這個(gè)順序就是它們被編寫在代碼中的順序,與處理器或其它因素?zé)o關(guān),這種模型被稱作順序一致性模型,也是基于馮·諾依曼體系的模型。當(dāng)然,這種假設(shè)本身是合理的,在實(shí)踐中也鮮有異常發(fā)生,但事實(shí)上,沒有哪個(gè)現(xiàn)代多處理器架構(gòu)會(huì)采用這種模型,因?yàn)樗窃谑翘托Я恕6诰幾g優(yōu)化和CPU流水線中,幾乎都涉及到指令重排序。

    編譯期重排序

    編譯期重排序的典型就是通過調(diào)整指令順序,在不改變程序語義的前提下,盡可能減少寄存器的讀取、存儲(chǔ)次數(shù),充分復(fù)用寄存器的存儲(chǔ)值。

    假設(shè)第一條指令計(jì)算一個(gè)值賦給變量A并存放在寄存器中,第二條指令與A無關(guān)但需要占用寄存器(假設(shè)它將占用A所在的那個(gè)寄存器),第三條指令使用A的值且與第二條指令無關(guān)。那么如果按照順序一致性模型,A在第一條指令執(zhí)行過后被放入寄存器,在第二條指令執(zhí)行時(shí)A不再存在,第三條指令執(zhí)行時(shí)A重新被讀入寄存器,而這個(gè)過程中,A的值沒有發(fā)生變化。通常編譯器都會(huì)交換第二和第三條指令的位置,這樣第一條指令結(jié)束時(shí)A存在于寄存器中,接下來可以直接從寄存器中讀取A的值,降低了重復(fù)讀取的開銷。

    重排序?qū)τ诹魉€的意義

    現(xiàn)代CPU幾乎都采用流水線機(jī)制加快指令的處理速度,一般來說,一條指令需要若干個(gè)CPU時(shí)鐘周期處理,而通過流水線并行執(zhí)行,可以在同等的時(shí)鐘周期內(nèi)執(zhí)行若干條指令,具體做法簡單地說就是把指令分為不同的執(zhí)行周期,例如讀取、尋址、解析、執(zhí)行等步驟,并放在不同的元件中處理,同時(shí)在執(zhí)行單元EU中,功能單元被分為不同的元件,例如加法元件、乘法元件、加載元件、存儲(chǔ)元件等,可以進(jìn)一步實(shí)現(xiàn)不同的計(jì)算并行執(zhí)行。

    流水線架構(gòu)決定了指令應(yīng)該被并行執(zhí)行,而不是在順序化模型中所認(rèn)為的那樣。重排序有利于充分使用流水線,進(jìn)而達(dá)到超標(biāo)量的效果。

    確保順序性

    盡管指令在執(zhí)行時(shí)并不一定按照我們所編寫的順序執(zhí)行,但毋庸置疑的是,在單線程環(huán)境下,指令執(zhí)行的最終效果應(yīng)當(dāng)與其在順序執(zhí)行下的效果一致,否則這種優(yōu)化便會(huì)失去意義。

    通常無論是在編譯期還是運(yùn)行期進(jìn)行的指令重排序,都會(huì)滿足上面的原則。

    Java存儲(chǔ)模型中的重排序

    在Java存儲(chǔ)模型(Java Memory Model, JMM)中,重排序是十分重要的一節(jié),特別是在并發(fā)編程中。JMM通過happens-before法則保證順序執(zhí)行語義,如果想要讓執(zhí)行操作B的線程觀察到執(zhí)行操作A的線程的結(jié)果,那么A和B就必須滿足happens-before原則,否則,JVM可以對它們進(jìn)行任意排序以提高程序性能。

    volatile關(guān)鍵字可以保證變量的可見性,因?yàn)閷olatile的操作都在Main Memory中,而Main Memory是被所有線程所共享的,這里的代價(jià)就是犧牲了性能,無法利用寄存器或Cache,因?yàn)樗鼈兌疾皇侨值模瑹o法保證可見性,可能產(chǎn)生臟讀。

    volatile還有一個(gè)作用就是局部阻止重排序的發(fā)生,對volatile變量的操作指令都不會(huì)被重排序,因?yàn)槿绻嘏判颍挚赡墚a(chǎn)生可見性問題。

    在保證可見性方面,鎖(包括顯式鎖、對象鎖)以及對原子變量的讀寫都可以確保變量的可見性。但是實(shí)現(xiàn)方式略有不同,例如同步鎖保證得到鎖時(shí)從內(nèi)存里重新讀入數(shù)據(jù)刷新緩存,釋放鎖時(shí)將數(shù)據(jù)寫回內(nèi)存以保數(shù)據(jù)可見,而volatile變量干脆都是讀寫內(nèi)存。







    posted @ 2015-03-22 13:27 abin 閱讀(343) | 評(píng)論 (0)編輯 收藏

    Java 內(nèi)存模型

    由于 ConcurrentHashMap 是建立在 Java 內(nèi)存模型基礎(chǔ)上的,為了更好的理解 ConcurrentHashMap,讓我們首先來了解一下 Java 的內(nèi)存模型。

    Java 語言的內(nèi)存模型由一些規(guī)則組成,這些規(guī)則確定線程對內(nèi)存的訪問如何排序以及何時(shí)可以確保它們對線程是可見的。下面我們將分別介紹 Java 內(nèi)存模型的重排序,內(nèi)存可見性和 happens-before 關(guān)系。

    重排序

    內(nèi)存模型描述了程序的可能行為。具體的編譯器實(shí)現(xiàn)可以產(chǎn)生任意它喜歡的代碼 -- 只要所有執(zhí)行這些代碼產(chǎn)生的結(jié)果,能夠和內(nèi)存模型預(yù)測的結(jié)果保持一致。這為編譯器實(shí)現(xiàn)者提供了很大的自由,包括操作的重排序。

    編譯器生成指令的次序,可以不同于源代碼所暗示的“顯然”版本。重排序后的指令,對于優(yōu)化執(zhí)行以及成熟的全局寄存器分配算法的使用,都是大有脾益的,它使得程序在計(jì)算性能上有了很大的提升。

    重排序類型包括:

    • 編譯器生成指令的次序,可以不同于源代碼所暗示的“顯然”版本。
    • 處理器可以亂序或者并行的執(zhí)行指令。
    • 緩存會(huì)改變寫入提交到主內(nèi)存的變量的次序。

    內(nèi)存可見性

    由于現(xiàn)代可共享內(nèi)存的多處理器架構(gòu)可能導(dǎo)致一個(gè)線程無法馬上(甚至永遠(yuǎn))看到另一個(gè)線程操作產(chǎn)生的結(jié)果。所以 Java 內(nèi)存模型規(guī)定了 JVM 的一種最小保證:什么時(shí)候?qū)懭胍粋€(gè)變量對其他線程可見。

    在現(xiàn)代可共享內(nèi)存的多處理器體系結(jié)構(gòu)中每個(gè)處理器都有自己的緩存,并周期性的與主內(nèi)存協(xié)調(diào)一致。假設(shè)線程 A 寫入一個(gè)變量值 V,隨后另一個(gè)線程 B 讀取變量 V 的值,在下列情況下,線程 B 讀取的值可能不是線程 A 寫入的最新值:

    • 執(zhí)行線程 A 的處理器把變量 V 緩存到寄存器中。
    • 執(zhí)行線程 A 的處理器把變量 V 緩存到自己的緩存中,但還沒有同步刷新到主內(nèi)存中去。
    • 執(zhí)行線程 B 的處理器的緩存中有變量 V 的舊值。

    Happens-before 關(guān)系

    happens-before 關(guān)系保證:如果線程 A 與線程 B 滿足 happens-before 關(guān)系,則線程 A 執(zhí)行動(dòng)作的結(jié)果對于線程 B 是可見的。如果兩個(gè)操作未按 happens-before 排序,JVM 將可以對他們?nèi)我庵嘏判颉?/p>

    下面介紹幾個(gè)與理解 ConcurrentHashMap 有關(guān)的 happens-before 關(guān)系法則:

    1. 程序次序法則:如果在程序中,所有動(dòng)作 A 出現(xiàn)在動(dòng)作 B 之前,則線程中的每動(dòng)作 A 都 happens-before 于該線程中的每一個(gè)動(dòng)作 B。
    2. 監(jiān)視器鎖法則:對一個(gè)監(jiān)視器的解鎖 happens-before 于每個(gè)后續(xù)對同一監(jiān)視器的加鎖。
    3. Volatile 變量法則:對 Volatile 域的寫入操作 happens-before 于每個(gè)后續(xù)對同一 Volatile 的讀操作。
    4. 傳遞性:如果 A happens-before 于 B,且 B happens-before C,則 A happens-before C。
    posted @ 2015-03-22 12:40 abin 閱讀(1091) | 評(píng)論 (0)編輯 收藏

    1.希望復(fù)用一些現(xiàn)存的類,但是接口又與復(fù)用環(huán)境要求不一致。
    2.其實(shí)適配器模式有點(diǎn)無奈之舉,在前期設(shè)計(jì)的時(shí)候,我們就不應(yīng)該考慮適配器模式,而應(yīng)該考慮通過重構(gòu)統(tǒng)一接口。

    想使用一個(gè)已存在的類,但是該類不符合接口需求;或者需要?jiǎng)?chuàng)建一個(gè)可重用的類,適配沒有提供合適接口的其它類。
    適配器模式主要解決的問題就是我們要調(diào)用的接口類型,無法滿足我們新系統(tǒng)的使用需求,這時(shí)候,我們需要將舊系統(tǒng)的接口,通過適配器進(jìn)行轉(zhuǎn)配,達(dá)到支持新接口調(diào)用的目的。

    對于這樣的要求,我們通過適配器就可以完成,當(dāng)然如果有多個(gè)接口需要轉(zhuǎn)配,那么我們就需要為每一個(gè)接口提供一個(gè)適配器去完成轉(zhuǎn)換的工作。當(dāng)然具體的調(diào)用過程,我們可以進(jìn)行相應(yīng)的封裝。達(dá)到比較通用的方式去調(diào)用適配器,完成適配服務(wù)。
    我們來看看適配的過程。 我們根據(jù)上面的適配器的特點(diǎn)的介紹中,我們來分析下適配器模式的幾類比較適用的使用場景:
    1、我們在使用第三方的類庫,或者說第三方的API的時(shí)候,我們通過適配器轉(zhuǎn)換來滿足現(xiàn)有系統(tǒng)的使用需求。
    2、我們的舊系統(tǒng)與新系統(tǒng)進(jìn)行集成的時(shí)候,我們發(fā)現(xiàn)舊系統(tǒng)的數(shù)據(jù)無法滿足新系統(tǒng)的需求,那么這個(gè)時(shí)候,我們可能需要適配器,完成調(diào)用需求。
    3、我們在使用不同數(shù)據(jù)庫之間進(jìn)行數(shù)據(jù)同步。(我這里只是分析的是通過程序來說實(shí)現(xiàn)的時(shí)候的情況。還有其他的很多種方式[數(shù)據(jù)庫同步])。 我們本節(jié)給出適配器模式的經(jīng)典實(shí)現(xiàn)代碼,我們這里結(jié)合項(xiàng)目中的查詢服務(wù)來進(jìn)行說明,舊系統(tǒng)中提供一個(gè)查詢服務(wù)方法Query();但是我新系統(tǒng)定義底層的數(shù)據(jù)訪問服務(wù)層 的時(shí)候,卻是使用的GetList()方法,并且將之前的返回結(jié)果集合進(jìn)行包裝成泛型的形式來進(jìn)行。
    posted @ 2015-03-18 13:59 abin 閱讀(1972) | 評(píng)論 (0)編輯 收藏

    枚舉單例模式:
    關(guān)于單例模式的實(shí)現(xiàn)有很多種,網(wǎng)上也分析了如今實(shí)現(xiàn)單利模式最好用枚舉,好處不外乎三點(diǎn):
    1.線程安全 2.不會(huì)因?yàn)樾蛄谢a(chǎn)生新實(shí)例 3.防止反射攻擊
    1.線程安全 
    下面這段代碼就是聲明枚舉實(shí)例的通常做法,它可能還包含實(shí)例變量和實(shí)例方法,但是為了簡單起見,我并沒有使用這些東西,僅僅需要小心的是如果你正在使用實(shí)例方法,那么你需要確保線程安全(如果它影響到其他對象的狀態(tài)的話)。默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,但是在枚舉中的其他任何方法由程序員自己負(fù)責(zé)。
    關(guān)于線程安全的保證,其實(shí)是通過類加載機(jī)制來保證的,我們看看INSTANCE的實(shí)例化時(shí)機(jī),是在static塊中,JVM加載類的過程顯然是線程安全的。
    static {};
      Code:
       0:   new     #12; //class com/abin/lee/spring/util/Singleton$1
       3:   dup
       4:   ldc     #14; //String INSTANCE
       6:   iconst_0
       7:   invokespecial   #15; //Method com/abin/lee/spring/util/Singleton$1."<init>":(Ljava/lang/String;I)V
       10:  putstatic       #19; //Field INSTANCE:Lcom/abin/lee/spring/util/Singleton;
       13:  iconst_1
       14:  anewarray       #1; //class com/abin/lee/spring/util/Singleton
       17:  dup
       18:  iconst_0
       19:  getstatic       #19; //Field INSTANCE:Lcom/abin/lee/spring/util/Singleton;
       22:  aastore
       23:  putstatic       #21; //Field ENUM$VALUES:[Lcom/abin/lee/spring/util/Singleton;
       26:  return
    線程安全,從反編譯后的類源碼中可以看出也是通過類加載機(jī)制保證的,應(yīng)該是這樣吧

    2.不會(huì)因?yàn)樾蛄谢a(chǎn)生新實(shí)例
    枚舉自己處理序列化
    傳統(tǒng)單例存在的另外一個(gè)問題是一旦你實(shí)現(xiàn)了序列化接口,那么它們不再保持單例了,因?yàn)閞eadObject()方法一直返回一個(gè)新的對象就像java的構(gòu)造方法一樣,你可以通過使用readResolve()方法來避免此事發(fā)生,看下面的例子:
    //readResolve to prevent another instance of Singleton
        private Object readResolve(){
            return INSTANCE;
        }
    這樣甚至還可以更復(fù)雜,如果你的單例類維持了其他對象的狀態(tài)的話,因此你需要使他們成為transient的對象。但是枚舉單例,JVM對序列化有保證。
    優(yōu)點(diǎn):不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象


    3.防止反射攻擊
    反射攻擊,我有自己試著反射攻擊了以下,不過報(bào)錯(cuò)了...看了下方的反編譯類源碼,明白了,因?yàn)閱卫惖男揎検莂bstract的,所以沒法實(shí)例化。(解決













    靜態(tài)內(nèi)部類:
    // Correct lazy initialization in Java 
    @ThreadSafe
    class Foo {
        private static class HelperHolder {
           public static Helper helper = new Helper();
        }
     
        public static Helper getHelper() {
            return HelperHolder.helper;
        }
    }

    它利用了內(nèi)部靜態(tài)類只有在被引用的時(shí)候才會(huì)被加載的規(guī)律。

    這樣一來,一旦內(nèi)部的HelperHolder被引用了,它就會(huì)首先被JVM加載,進(jìn)行該類的靜態(tài)域的初始化,從而使得Helper這一單例類被初始化。它之所以是線程安全的,也是托了JVM的福,因?yàn)?/span>JVM對于類的加載這一過程是線程安全的。

    posted @ 2015-03-17 15:15 abin 閱讀(366) | 評(píng)論 (0)編輯 收藏

         在看 Java并發(fā)包(java.util.concurrent)中大量使用了CAS操作,CAS即 Compare And Swap(CAS),比較并交換,其中由sun.misc.Unsafe實(shí)現(xiàn),Unsafe類提供了硬件級(jí)別的原子操作,Java無法直接訪問到操作系統(tǒng)底層(如系統(tǒng)硬件等),為此Java使用native方法來擴(kuò)展Java程序的功能。具體實(shí)現(xiàn)使用c++。 牛B,**Unsafe類提供了硬件級(jí)別的原子操作**,但到底是什么意思呢?CAS從字面意思上就有兩部分:1、讀取并比較;2、判斷并決定是否交換。然后把這兩步驟作為原子操作,這樣就能保證線程安全
         幾句關(guān)于Unsafe的并發(fā)性。compareAndSwap方法是原子的,并且可用來實(shí)現(xiàn)高性能的、無鎖的數(shù)據(jù)結(jié)構(gòu)。比如,考慮問題:在使用大量線程的共享對象上增長值。...
         Java是一門安全的編程語言,防止程序員犯很多愚蠢的錯(cuò)誤,它們大部分是基于內(nèi)存管理的。但是,有一種方式可以有意的執(zhí)行一些不安全、容易犯錯(cuò)的操作,那就是使用Unsafe類。
        CAS操作有3個(gè)操作數(shù),內(nèi)存值M,預(yù)期值E,新值U,如果M==E,則將內(nèi)存值修改為B,否則啥都不做。
        sun.misc.Unsafe這個(gè)類是如此地不安全,以至于JDK開發(fā)者增加了很多特殊限制來訪問它。它的構(gòu)造器是私有的,工廠方法getUnsafe()的調(diào)用器只能被Bootloader加載。如你在下面代碼片段的第8行所見,這個(gè)家伙甚至沒有被任何類加載器加載,所以它的類加載器是null。它會(huì)拋出SecurityException 異常來阻止侵入者。
        public final class Unsafe {
       ...
       private Unsafe() {}
       private static final Unsafe theUnsafe = new Unsafe();
       ...
       public static Unsafe getUnsafe() {
          Class cc = sun.reflect.Reflection.getCallerClass(2);
          if (cc.getClassLoader() != null)
              throw new SecurityException("Unsafe");
          return theUnsafe;
       }
       ...
    }
    幸運(yùn)的是這里有一個(gè)Unsafe的變量可以被用來取得Unsafe的實(shí)例。我們可以輕松地編寫一個(gè)復(fù)制方法通過反射來實(shí)現(xiàn),如下所示:
    http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/
    public static Unsafe getUnsafe() {
       try {
               Field f = Unsafe.class.getDeclaredField("theUnsafe");
               f.setAccessible(true);
               return (Unsafe)f.get(null);
       } catch (Exception e) {
           /* ... */
       }
    }

    Unsafe一些有用的特性

    1. 虛擬機(jī)“集約化”(VM intrinsification):如用于無鎖Hash表中的CAS(比較和交換)。再比如compareAndSwapInt這個(gè)方法用JNI調(diào)用,包含了對CAS有特殊引導(dǎo)的本地代碼。在這里你能讀到更多關(guān)于CAS的信息:http://en.wikipedia.org/wiki/Compare-and-swap
    2. 主機(jī)虛擬機(jī)(譯注:主機(jī)虛擬機(jī)主要用來管理其他虛擬機(jī)。而虛擬平臺(tái)我們看到只有g(shù)uest VM)的sun.misc.Unsafe功能能夠被用于未初始化的對象分配內(nèi)存(用allocateInstance方法),然后將構(gòu)造器調(diào)用解釋為其他方法的調(diào)用。
    3. 你可以從本地內(nèi)存地址中追蹤到這些數(shù)據(jù)。使用java.lang.Unsafe類獲取內(nèi)存地址是可能的。而且可以通過unsafe方法直接操作這些變量!
    4. 使用allocateMemory方法,內(nèi)存可以被分配到堆外。例如當(dāng)allocateDirect方法被調(diào)用時(shí)DirectByteBuffer構(gòu)造器內(nèi)部會(huì)使用allocateMemory
    5. arrayBaseOffsetarrayIndexScale方法可以被用于開發(fā)arraylets,一種用來將大數(shù)組分解為小對象、限制掃描的實(shí)時(shí)消耗或者在大對象上做更新和移動(dòng)。

    Unsafe.java是jdk并發(fā)類庫java.util.concurrent包中使用的底層類,該類中的方法都是通過native方法調(diào)用操作系統(tǒng)命令。

        在多線程環(huán)境下,主要存在兩種特殊的場景:

        1;多個(gè)線程同時(shí)訪問某個(gè)對象的方法,由于操作系統(tǒng)對線程調(diào)度的不確定性,存在使該對象的狀態(tài)處于不一致的狀態(tài),為了避免這種情況的發(fā)生,我們可以聲明該方法為同步方法(synchronized)。保證某一時(shí)刻只有一個(gè)線程調(diào)用該方法,其它調(diào)用線程則被阻塞。這種方法在并發(fā)性很高的情況下會(huì)產(chǎn)生大量的線程調(diào)度開銷。從而影響程序的并發(fā)性能。在java.util.concurrent包中通過使用非阻塞原子方法,減少操作系統(tǒng)的無用線程調(diào)度開銷,從而提高并發(fā)性能。

       2;多線程通過某個(gè)對象進(jìn)行通信,即常見的生產(chǎn)者消費(fèi)者模型。在以前的jdk版本中是通過wait,notify方法實(shí)現(xiàn)的。該方法也是通過底層在某個(gè)信號(hào)量上的阻塞隊(duì)列實(shí)現(xiàn)的。而Unsafe類中直接提供操作系統(tǒng)調(diào)度命令park,unpark,減少信號(hào)量的開銷,提高新能。

       從上面可以看出,Unsafe類是通過提供底層的操作系統(tǒng)命令接口給開發(fā)者,從而提供程序的性能。



    posted @ 2015-03-15 23:42 abin 閱讀(1821) | 評(píng)論 (0)編輯 收藏

    僅列出標(biāo)題
    共50頁: First 上一頁 3 4 5 6 7 8 9 10 11 下一頁 Last 
    主站蜘蛛池模板: 久久国产精品免费看| 一区二区三区精品高清视频免费在线播放 | 日本黄色免费观看| 亚洲狠狠成人综合网| 91情侣在线精品国产免费| 亚洲中字慕日产2021| 国产人在线成免费视频| 亚洲国产最大av| 免费高清资源黄网站在线观看 | 亚洲精品国产精品国自产观看 | 黑人大战亚洲人精品一区| 精品久久久久久无码免费| 亚洲中文久久精品无码| 男人进去女人爽免费视频国产| 国产v亚洲v天堂无码网站| 99re在线免费视频| 亚洲人成www在线播放| 成年在线观看免费人视频草莓| 亚洲欧美日韩自偷自拍| 亚洲成年人啊啊aa在线观看| 久久精品无码专区免费| 久久精品亚洲综合| 一个人看www在线高清免费看| 亚洲成a∧人片在线观看无码| 亚洲不卡无码av中文字幕| 免费国产污网站在线观看| 亚洲视频日韩视频| 手机看片久久国产免费| 国产黄色片免费看| 亚洲成在人线中文字幕| 日韩伦理片电影在线免费观看| 成人午夜影视全部免费看| 亚洲一本综合久久| 日韩特黄特色大片免费视频| 九九热久久免费视频| 亚洲毛片免费视频| 亚洲另类少妇17p| 两性刺激生活片免费视频| 四虎影视久久久免费观看| 亚洲黄色免费网址| 亚洲国产香蕉人人爽成AV片久久|