日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

我們經常在很多項目里面看到用異常來處理業務邏輯,發現不符合預期直接拋出異常,然后在最外面捕獲異常統一處理,這樣使用非常方便。

但是又有很多文章寫著異常處理性能,所以不建議使用異常來做流程控制。甚至在阿里巴巴開發手冊里面明確說明了,不要用來做流程控制。

 

那么問題來了:

究竟能不能用異常來做流程控制?效率低是低多少?看完這一篇文章你就知道了。

開始測試

先做最簡單的測試

我們循環10萬次,然后棧有5層,然后輸出返回結果。

private static final int RUN_COUNT = 10 * 10000;

    /**
     * 測試異常耗時
     * 輸出異常堆棧&信息
     */
    @Test
    public void printStack() {
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            log.info(Storey1.test());
        }

        long start2 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            try {
                Storey1.testException();
            } catch (Exception e) {
                log.info(e.getMessage(), e);
            }
        }
        long end = System.currentTimeMillis();
        log.info("普通返回耗時:{},異常返回耗時:{}", start2 - start1, end - start1);
    }
    public static class Storey1 {
        public static String test() {
            return Storey2.test();
        }

        public static String testException() {
            return Storey2.testException();
        }
    }

    public static class Storey2 {
        public static String test() {
            return Storey3.test();
        }

        public static String testException() {
            return Storey3.testException();
        }
    }

    public static class Storey3 {
        public static String test() {
            return Storey4.test();
        }

        public static String testException() {
            return Storey4.testException();
        }
    }

    public static class Storey4 {
        public static String test() {
            return Storey5.test();
        }

        public static String testException() {
            return Storey5.testException();
        }
    }

    public static class Storey5 {
        public static String test() {
            return Integer.toString(count++);
        }

        public static String testException() {
            throw new CustomException(Integer.toString(count++));
        }
    }


    public static class CustomException extends RuntimeException {

        public CustomException(String message) {
            super(message);
        }

    }

結果差別很大,普通返回只要2137毫秒,而異常卻要75026毫秒,幾十倍的差距。

15:07:59.648 [main] INFO com.alibaba.easytools.test.temp.exception.ExceptionTest - 普通返回耗時:2137,異常返回耗時:75026

不輸出堆棧信息

聰明的同學不難發現,上面有個變量沒控制住,就是使用異常的情況下,輸出了堆棧信息,那我們關閉堆棧輸出試試。會不會是輸出的堆棧信息導致的慢呢?

  /**
     * 測試異常耗時
     * 僅僅輸出信息
     */
    @Test
    public void print() {
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            log.info(Storey1.test());
        }

        long start2 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            try {
                Storey1.testException();
            } catch (Exception e) {
                log.info(e.getMessage());
            }
        }
        long end = System.currentTimeMillis();
        log.info("普通返回耗時:{},異常返回耗時:{}", start2 - start1, end - start1);
    }

結果發現普通返回是2053毫秒,而異常卻要4380毫秒,發現差距瞬間變小了。

15:43:54.260 [main] INFO com.alibaba.easytools.test.temp.exception.ExceptionTest - 普通返回耗時:2053,異常返回耗時:4380

不輸出任何信息

顯然我們發現,關閉了日志輸出對執行時間影像很大,那我們關閉了日志輸出會有什么效果了呢?

    /**
     * 測試異常耗時
     * 不輸出信息
     */
    @Test
    public void noPrint() {
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            Storey1.test();
        }

        long start2 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            try {
                Storey1.testException();
            } catch (Exception e) {
            }
        }
        long end = System.currentTimeMillis();
        log.info("普通返回耗時:{},異常返回耗時:{}", start2 - start1, end - start1);
    }

結果發現普通返回是58毫秒,而異常卻要719毫秒,看來性能實際差距就是十幾倍。

15:47:55.901 [main] INFO com.alibaba.easytools.test.temp.exception.ExceptionTest - 普通返回耗時:58,異常返回耗時:719

關閉堆棧

在處理異常的時候,很多時間在封裝異常堆棧,那有沒有辦法可以不要封裝呢?

仔細研究異常類發現,異常類有個參數`writableStackTrace` 可以讓異常不去封裝堆棧信息。

public class RuntimeException extends Exception {

    /**
     * Constructs a new runtime exception with the specified detail
     * message, cause, suppression enabled or disabled, and writable
     * stack trace enabled or disabled.
     *
     * @param  message the detail message.
     * @param cause the cause.  (A {@code null} value is permitted,
     * and indicates that the cause is nonexistent or unknown.)
     * @param enableSuppression whether or not suppression is enabled
     *                          or disabled
     * @param writableStackTrace whether or not the stack trace should
     *                           be writable
     *
     * @since 1.7
     */
    protected RuntimeException(String message, Throwable cause,
                               boolean enableSuppression,
                               boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

我們把拋異常的時候不去封裝異常信息

    /**
     * 測試異常耗時
     * 關閉堆棧 并且不打印
     */
    @Test
    public void noPrintCloseStackTrace() {
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            Storey1.test();
        }

        long start2 = System.currentTimeMillis();
        for (int i = 0; i < RUN_COUNT; i++) {
            try {
                Storey1.testException();
            } catch (Exception e) {
            }
        }
        long end = System.currentTimeMillis();
        log.info("普通返回耗時:{},異常返回耗時:{}", start2 - start1, end - start1);
    }

    public static class Storey5 {
        public static String test() {
            return Integer.toString(count++);
        }

        public static String testException() {            throw new CustomException(Integer.toString(count++), null, false, false);
        }
    }

    public static class CustomException extends RuntimeException {

        public CustomException(String message) {
            super(message);
        }

        public CustomException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }

結果發現普通返回是31毫秒,而異常卻要62毫秒,差距也沒有想象中的大了。差不多是2倍左右。

15:54:26.984 [main] INFO com.alibaba.easytools.test.temp.exception.ExceptionTest - 普通返回耗時:31,異常返回耗時:62

最終結果

我們來看下最終對比結論

 

普通

異常

普通輸出日志,異常輸出堆棧

2137

75026

普通輸出日志,異常僅輸出日志

2053

4380

都不輸出日志

58

719

關閉堆棧

31

62

結論

所以我們可以總結出以下結論

  • 日志輸出堆棧非常耗時
  • 哪怕日志只是輸出業務邏輯,耗時和業務處理也不是一個時間維度的
  • 排出日志影響,封裝堆棧非常耗時
  • 關閉堆棧以后耗時相差不大,大概1萬次相差3毫秒

三種方式優缺點總結下:

 

優點

缺點

普通

  • 最快速
  • 嵌套深了,或者有返回值的情況下代碼會比較丑陋
  • 如果打印了請求日志,多個地方返回相同值,不好排查

關閉堆棧的異常

  • 相對速度還行
  • 代碼相對書寫比較方便
  • 有時候不打印堆棧多個地方返回相同值,不好排查

不關閉堆棧的異常

  • 代碼相對書寫比較方便
  • 能打印整個堆棧信息,非常容易定位問題
  • 速度慢

如果我們的并發沒有大到必須關閉日志這種情況下,實際上來說異常來控制流程問題不大,影響微乎其微,所以還是怎么方便怎么來。

當然如果項目并發超級高,高到單機1萬次請求要省3毫秒的情況下,建議還是用返回去控制業務流程。

分享到:
標簽:Java
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定