阿里巴巴Java開發規范手冊(三)


(六)並發處理

  1、獲取單例對象須要保證線程安全,其中的方法也要保證線程安全

  2、創建線程或線程池時 請指定有意義的線程名稱,方便出錯時回溯

  3、線程資源必須通過線程池提供,不允許在應用中自行顯示創建線程

  4、線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,

      這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險

    (1)、FixedThreadPool 和SingleThreadPool

      允許的請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM

    (2)、CachedThreadPool和ScheduleThreadPool

      允許的創建線程數量為Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM

  5、SimpleDateFormat是線程不安全的類,一般不要定義為static變量,

    如果定義為static必須加鎖,或者使用DateUtils工具類

  6、高並發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖

    能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖

  7、對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,

    否則可能會造成死鎖

  8、並發修改同一記錄時,避免更新丟失,需要加鎖。要么在應用層加鎖,要么在緩存加鎖,

    要么在數據庫層使用樂觀鎖,使用version作為更新依據

  9、多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的

    異常,其他任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題

  10、使用CountDownLatch進行異步轉同步操作,每個線程退出前必須調用countDown方法,

    線程執行代碼注意catch異常,確保countDown方法被執行到,避免主線程無法執行至

    await方法,直到超時才返回結果

  11、避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一個

    seed導致的性能下降

  12、在並發場景下,通過雙重檢查鎖(double-checked locking)實現延遲初始化的優化問題

    隱患,將目標屬性聲明為volatile

  13、volatile解決多線程內存不可見問題。對於一寫多讀,是可以解決變量同步問題,但是

    如果多寫,同樣無法解決線程安全問題。如果是 count++操作,使用如下類實現:

    AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推
    薦使用 LongAdder 對象,比 AtomicLong 性能更好(減少樂觀鎖的重試次數)。
  14、HashMap 在容量不夠進行 resize 時由於高並發可能出現死鏈,導致 CPU 飆升,在
      開發過程中可以使用其它數據結構或加鎖來規避此風險。
  15、ThreadLocal 無法解決共享對象的更新問題,ThreadLocal 對象建議使用 static修飾。

    這個變量是針對一個線程內所有操作共享的,所以設置為靜態變量,所有此類實例共享
    此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,所有此類的對

    象(只要是這個線程內定義的)都可以操控這個變量。

(七)、控制語句

  1、在一個switch塊內,每一個case要么通過break/return等來終止,要么注釋說明程序將

    繼續執行到哪一個case為止;在一個switch塊內,都必須包含一個default語句並且放在

    最后,即使它什么代碼都沒有

  2、在 if/else/for/while/do 語句中必須使用大括號。即使只有一行代碼,避免采用單行的編碼

    方式:if (condition) statements

  3、表達異常的分支時,少用 if-else 方式,這種方式可以改寫成:

     if (condition) { 

        ... 

        return obj;
      } 
      // 接着寫 else 的業務邏輯代碼;
    說明:如果非得使用 if()...else if()...else...方式表達邏輯,避免后續代碼維護困難,

    請勿超過 3 層。

    正例:超過 3 層的 if-else 的邏輯判斷代碼可以使用衛語句、策略模式、狀態模式等

        來實現,其中衛語句示例如下:

       public void today() {

         if (isBusy()) {

            System.out.println(“change time.”);

            return;

         }

         if (isFree()) {

             System.out.println(“go to travel.”);

             return;

         }

         System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);

         return;
                         }

  4、除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行其它復雜的語句,將復

    雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。

    說明:很多 if 語句內的邏輯相當復雜,閱讀者需要分析條件表達式的最終結果,

    才能明確什么樣的條件執行什么樣的語句,那么,如果閱讀者分析邏輯表達式錯誤呢?

    正例:
      // 偽代碼如下
      final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
      if (existed) {
        ...
      }

  5、循環體中的語句要考量性能,以下操作盡量移至循環體外處理,如定義對象、變量、

    獲取數據庫連接,進行不必要的 try-catch 操作(這個 try-catch 是否可以移至循環體外)

  6、接口入參保護,這種場景常見的是用於做批量操作的接口

  7、下列情形,需要進行參數校驗:

    (1) 調用頻次低的方法。
    (2) 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因為

      參數錯誤導致中間執行回退,或者錯誤,那得不償失。

    (3) 需要極高穩定性和可用性的方法。
    (4) 對外提供的開放接口,不管是 RPC/API/HTTP 接口。
    (5) 敏感權限入口。

  8、下列情形,不需要進行參數校驗:

    (1) 極有可能被循環調用的方法。但在方法說明里必須注明外部參數檢查要求。
    (2) 底層調用頻度比較高的方法。畢竟是像純凈水過濾的最后一道,參數錯誤不太可能到

      底層才會暴露問題。一般 DAO 層與 Service 層都在同一個應用中,部署在同一台

      服務器中,所以 DAO 的參數校驗,可以省略。
    (3) 被聲明成 private 只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入

      參數已經做過檢查或者肯定不會有問題,此時可以不校驗參數。

(八)注釋規則

  1、類、類屬性、類方法的注釋必須使用 Javadoc 規范,使用/**內容*/格式,不得使用

    // xxx 方式。

  2、所有的抽象方法(包括接口中的方法)必須要用 Javadoc 注釋、除了返回值、參數、

    異常說明外,還必須指出該方法做什么事情,實現什么功能

  3、所有的類都必須添加創建者和創建日期。

  4、方法內部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內部多行注釋

    使用/* */注釋,注意與代碼對齊。

  5、所有的枚舉類型字段必須要有注釋,說明每個數據項的用途

  6、與其“半吊子”英文來注釋,不如用中文注釋把問題說清楚。

    專有名詞與關鍵字保持英文原文即可

  7、代碼修改的同時,注釋也要進行相應的修改,尤其是參數、返回值、異常、

    核心邏輯等的修改

  8、謹慎注釋掉代碼。在上方詳細說明,而不是簡單地注釋掉。如果無用,則刪除

  9、對於注釋的要求:第一、能夠准確反應設計思想和代碼邏輯;

    第二、能夠描述業務含義,使別的程序員能夠迅速了解到代碼背后的信息。完全沒有注釋

    的大段代碼對於閱讀者形同天書,注釋是給自己看的,即使隔很長時間,也能清晰理解當

    時的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作

  10、好的命名、代碼結構是自解釋的,注釋力求精簡准確、表達到位。避免出現注釋的

    一個極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當大的負擔

  11、特殊注釋標記,請注明標記人與標記時間。注意及時處理這些標記,通過標記掃描,

    經常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。

    (1) 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間])

      表示需要實現,但目前還未實現的功能。這實際上是一個 Javadoc 的標簽,

      目前的 Javadoc還沒有實現,但已經被廣泛使用。只能應用於類,接口和方法

     (因為它是一個 Javadoc 標簽)。
    (2) 錯誤,不能工作(FIXME):(標記人,標記時間,[預計處理時間])

       在注釋中用 FIXME 標記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。

(九)其它

  1、在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度。

  2、velocity 調用 POJO 類的屬性時,建議直接使用屬性名取值即可,模板引擎會自動按

    規范調用 POJO 的 getXxx(),如果是 boolean 基本數據類型變量(boolean 命名不需要加     is前綴),會自動調用 isXxx()方法

  3、后台輸送給頁面的變量必須加$!{var}——中間的感嘆號

  4、注意 Math.random() 這個方法返回是 double 類型,注意取值的范圍 0≤x<1

   (能夠取到零值,注意除零異常),如果想獲取整數類型的隨機數,不要將 x 放大 10 的

    若干倍然后取整,直接使用 Random 對象的 nextInt 或者 nextLong 方法

  5、獲取當前毫秒數 System.currentTimeMillis(); 而不是 new Date().getTime();

    說明:如果想獲取更加精確的納秒級時間值,使用 System.nanoTime()的方式。

    在 JDK8 中,針對統計時間等場景,推薦使用 Instant 類

  6、不要在視圖模板中加入任何復雜的邏輯。

  7、任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增長吃光內存

  8、及時清理不再使用的代碼段或配置信息。

 


出自《阿里巴巴Java開發手冊》 喜歡請支持正版 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM