szhswl
          宋針還的個(gè)人空間
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

              blog是收集資料并且作為技術(shù)交流的平臺(tái),發(fā)布一些本人常用資料或開(kāi)發(fā)經(jīng)驗(yàn),希望能和大家一起討論、進(jìn)步。

          訪問(wèn)統(tǒng)計(jì) free hit counter

          留言簿(6)

          我參與的團(tuán)隊(duì)

          隨筆檔案(8)

          文章分類(149)

          新聞分類(1)

          相冊(cè)

          收藏夾(21)

          友情鏈接

          我的鏈接

          搜索

          •  

          積分與排名

          • 積分 - 150772
          • 排名 - 413

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          跟蹤無(wú)法預(yù)期的運(yùn)行時(shí)異常可能是一件又慢又費(fèi)力的事情,只獲得默認(rèn)線程名稱和堆棧跟蹤通常是不夠的。在文中,Java 開(kāi)發(fā)人員 John Zukowski 向您展示了如何通過(guò)替代默認(rèn)行為來(lái)定制輸出。 他還對(duì)比了通過(guò)細(xì)分 ThreadGroup 定制輸出的老方法與通過(guò)提供自己的 UncaughtExceptionHandler 定制輸出的新方法。

            雖然我們不想創(chuàng)建在無(wú)法預(yù)期時(shí)拋出運(yùn)行時(shí)異常的程序,但這種情況還是會(huì)發(fā)生——尤其是第一次運(yùn)行復(fù)雜程序時(shí)。通常是使用默認(rèn)行為、打印堆棧溢出和結(jié)束線程的生命來(lái)處理這些異常。

            從哪里發(fā)現(xiàn)默認(rèn)行為?每個(gè)線程都屬于一個(gè)由 java.lang.ThreadGroup 類表示的線程組。顧名思義,線程組允許您將線程組合在一起。您可能是為了方便而將線程組合,例如,一個(gè)線程池中的所有線程都屬于組 X,而另一個(gè)池的所有線程則屬于組 Y,或者是為了訪問(wèn)控制而將線程進(jìn)行組合。組 X 中的線程無(wú)權(quán)訪問(wèn)或改變組 Y 中的線程,除非它們都在同一線程組內(nèi)(或在一個(gè)子組內(nèi))。

            在 Tiger 之前, ThreadGroup 類提供了一種處理未捕獲異常的方法: ThreadGroup 的 uncaughtException() 方法。如果異常不是 ThreadDeath ,則將線程的名稱和堆棧回溯(stack backtrace)發(fā)送到 System.err 。但是 Tiger 添加了另一種方法: Thread.UncaughtExceptionHandler 接口。細(xì)分 ThreadGroup 或安裝該新接口的實(shí)現(xiàn)都允許您更改默認(rèn)行為。我們將對(duì) Tiger 之前和之后提供的方法都進(jìn)行研究。

            使用 ThreadGroup 的定制行為

            發(fā)生未捕獲的異常時(shí),默認(rèn)行為是將堆棧溢出打印輸出到系統(tǒng)錯(cuò)誤( System.err )中,如清單 1 中所示。不需要使用任何命令參數(shù)來(lái)啟動(dòng)程序。

            清單 1. 線程溢出示例

          public class SimpleDump {
           public static void main(String args[]) {
            System.out.println(args[0]);
           }
          }

            不使用任何參數(shù)運(yùn)行該程序?qū)⑸汕鍐?2 中的輸出。盡管它不是一個(gè)很長(zhǎng)的堆棧跟蹤,但它是一個(gè)完整的堆棧跟蹤。

            清單 2. 默認(rèn)線程溢出輸出

          Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
          at SimpleDump.main(SimpleDump.java:3)

            正如 Java 平臺(tái)的許多東西一樣,如果不喜歡默認(rèn)行為,您可以對(duì)其進(jìn)行更改。在 Java 平臺(tái)的 Tiger 版以前的版本中,不能替代所有線程的默認(rèn)行為,但是可以創(chuàng)建一個(gè)新的 ThreadGroup ,并更改在該組內(nèi)創(chuàng)建的任何線程的默認(rèn)行為。您可以重寫(xiě) uncaughtException(Thread t, Throwable e) 方法來(lái)定制該行為。然后,當(dāng)發(fā)生未預(yù)料的運(yùn)行時(shí)異常時(shí),該線程組內(nèi)創(chuàng)建的任何線程都將獲得新的行為。不過(guò),最好是修復(fù)基礎(chǔ)問(wèn)題,我將提供一個(gè)簡(jiǎn)單的示例,說(shuō)明更改默認(rèn)行為所必需的步驟。清單 3 展示了將執(zhí)行代碼放入新線程的調(diào)整過(guò)的測(cè)試程序:

            清單 3. 調(diào)整過(guò)的線程溢出示例

          public class WindowDump {
           public static void main(String args[]) throws Exception {
            ThreadGroup group = new LoggingThreadGroup("Logger");
            new Thread(group, "myThread") {
             public void run() {
              System.out.println(1 / 0);
             }
            }.start();
           }
          }

            LoggingThreadGroup 類是一個(gè)新的內(nèi)容,清單 4 中顯示了它的定義。為了進(jìn)行說(shuō)明,通過(guò)重寫(xiě) uncaughtException() 方法實(shí)現(xiàn)的特殊行為將在一個(gè)彈出窗口中顯示該異常,這項(xiàng)操作是在特殊 Handler 的幫助下使用 Java Logging API 來(lái)完成的。

            清單 4. LoggingThreadGroup 的定義

          import java.util.logging.*;

          public class LoggingThreadGroup extends ThreadGroup {
           private static Logger logger;
           public LoggingThreadGroup(String name) {
            super(name);
           }
           public void uncaughtException(Thread t, Throwable e) {
            // Initialize logger once
            if (logger == null) {
             logger = Logger.getLogger("example");
             Handler handler = LoggingWindowHandler.getInstance();
             logger.addHandler(handler);
            }
            logger.log(Level.WARNING, t.getName(), e);
           }
          }

            這里創(chuàng)建的定制 Handler 的類型為 LoggingWindowHandler ,該類型的定義在清單 5 中。處理程序使用了一個(gè)支持類 LoggingWindow ,該類將異常顯示在屏幕上。 清單 6 中顯示了該類的定義。 Handler 的 public void publish(LogRecord record) 方法實(shí)現(xiàn)了一些重要操作。其余操作大部分只與配置有關(guān)。

            清單 5. LoggingWindowHandler 的定義

          import java.util.logging.*;

          public class LoggingWindowHandler extends Handler {
           private static LoggingWindow window;
           private static LoggingWindowHandler handler;

           private LoggingWindowHandler() {
            configure();
            window = new LoggingWindow("Logging window...", 400, 200);
           }

           public static synchronized LoggingWindowHandler getInstance() {
            if (handler == null) {
             handler = new LoggingWindowHandler();
            }
            return handler;
           }

           /**
           * Get any configuration properties set
           */
           private void configure() {
            LogManager manager = LogManager.getLogManager();
            String className = getClass().getName();
            String level = manager.getProperty(className + ".level");
            setLevel((level == null) ? Level.INFO : Level.parse(level));
            String filter = manager.getProperty(className + ".filter");
            setFilter(makeFilter(filter));
            String formatter = manager.getProperty(className + ".formatter");
            setFormatter(makeFormatter(formatter));
           }

           private Filter makeFilter(String name) {
            Filter f = null;
            try {
             Class c = Class.forName(name);
             f = (Filter)c.newInstance();
            } catch (Exception e) {
             if (name != null) {
              System.err.println("Unable to load filter: " + name);
             }
            }
            return f;
           }

           private Formatter makeFormatter(String name) {
            Formatter f = null;
             try {
              Class c = Class.forName(name);
              f = (Formatter)c.newInstance();
             } catch (Exception e) {
              f = new SimpleFormatter();
             }
             return f;
            }

            // Overridden abstract Handler methods

            public void close() {
            }

            public void flush() {
           }

           /**
           * If record is loggable, format it and add it to window
           */
           public void publish(LogRecord record) {
            String message = null;
            if (isLoggable(record)) {
             try {
              message = getFormatter().format(record);
             } catch (Exception e) {
             reportError(null, e, ErrorManager.FORMAT_FAILURE);
             return;
            }
           try {
            window.addLogInfo(message);
           } catch (Exception e) {
            reportError(null, e, ErrorManager.WRITE_FAILURE);
           }
          }
          }
          }                            

            清單 6. LoggingWindow 的定義

          import java.awt.*;
          import javax.swing.*;

          public class LoggingWindow extends JFrame {
           private JTextArea textArea;
           
           public LoggingWindow(String title, final int width,
           final int height) {
            super(title);
            EventQueue.invokeLater(new Runnable() {
             public void run() {
              setSize(width, height);
              textArea = new JTextArea();
              JScrollPane pane = new JScrollPane(textArea);
              textArea.setEditable(false);
              getContentPane().add(pane);
              setVisible(true);
             }
            });
           }  

           public void addLogInfo(final String data) {
            EventQueue.invokeLater(new Runnable() {
             public void run() {
              textArea.append(data);
             }
            });
           }
          }

            執(zhí)行 清單 3 中的 WindowDump 程序?qū)⒊霈F(xiàn)屏幕。因?yàn)闆](méi)有從 Logger 中刪除控制臺(tái)處理程序,所以堆棧溢出仍將出現(xiàn)在控制臺(tái)上。

            發(fā)生運(yùn)行時(shí)異常時(shí),可能要做許多工作來(lái)更改發(fā)生的問(wèn)題。該代碼的大部分都是 Logging Handler,但是,要執(zhí)行更改,就必須細(xì)分 ThreadGroup ,重寫(xiě) uncaughtException() ,然后在該線程組中執(zhí)行您的線程。不過(guò),讓我們通過(guò)只安裝 Thread.UncaughtExceptionHandler ,來(lái)看一看 Tiger 的處理方式。

            使用 UncaughtExceptionHandler 的定制行為

            對(duì)于 Tiger, Thread 類定義中添加了一個(gè)新的公共內(nèi)部類 UncaughtExceptionHandler ,更完整的名稱為 Thread.UncaughtExceptionHandler (其他類訪問(wèn)內(nèi)部類時(shí)需要使用完整名稱)。接口的定義是一個(gè)方法,如圖 7 中所示:

            清單 7. UncaughtExceptionHandler 的定義

          public interface Thread.UncaughtExceptionHandler {
           public void uncaughtException(Thread, Throwable);
          }

            您可能沒(méi)有注意到,清單 7 中的方法與我們前面重寫(xiě)的 ThreadGroup 的方法相同。實(shí)際上,現(xiàn)在由 ThreadGroup 類實(shí)現(xiàn)該接口。

            新的內(nèi)部類可以幫助我們了解下列兩對(duì)新方法,并有助于我們?cè)?Thread 中使用它們:

          getUncaughtExceptionHandler() 和 setUncaughtExceptionHandler() 。
          getDefaultUncaughtExceptionHandler() 和 setDefaultUncaughtExceptionHandler() 。

            第一對(duì)方法是 getUncaughtExceptionHandler() 和 setUncaughtExceptionHandler() ,它們?cè)试S您為當(dāng)前線程及其后代定制行為,從而允許二十或更多的線程擁有自己的定制行為。不過(guò),您更可能使用第二對(duì)方法 getDefaultUncaughtExceptionHandler() 和 setDefaultUncaughtExceptionHandler() 。如果使用第二對(duì)方法設(shè)置默認(rèn)處理程序,那么沒(méi)有自己的異常處理程序的所有線程都將使用默認(rèn)處理程序。

            聽(tīng)起來(lái)好像很簡(jiǎn)單。為了進(jìn)行說(shuō)明,清單 8 轉(zhuǎn)換了 清單 3 中的 ThreadGroup 友好的程序,使用新的 UncaughtExceptionHandler 接口:

          清單 8. UncaughtExceptionHandler 示例

          public class HandlerDump {
           public static void main(String args[]) throws Exception {
            Thread.UncaughtExceptionHandler handler = new LoggingThreadGroup("Logger");
            Thread.currentThread().setUncaughtExceptionHandler(handler);
            System.out.println(1 / 0);
           }
          }

            該程序只是將 LoggingThreadGroup 重用為 UncaughtExceptionHandler ,并沒(méi)有創(chuàng)建新的處理程序?qū)崿F(xiàn)。請(qǐng)注意,與原來(lái)的代碼相比,新代碼要簡(jiǎn)潔得多。

            其他線程更改

            Thread 類不僅支持使用 Tiger 添加的未捕獲異常處理程序,它還支持使用 getAllStackTraces() 獲得所有有效線程的堆棧跟蹤,或者支持使用 getStackTrace() 來(lái)只獲得當(dāng)前線程的堆棧跟蹤。這兩種堆棧跟蹤都返回類型為 java.lang.StackTraceElement 的對(duì)象, java.lang.StackTraceElement 是 Java 1.4 平臺(tái)中添加的一個(gè)類,它可以讓您生成自己的堆棧跟蹤。同時(shí),Java 5 平臺(tái)新添加的功能是一個(gè)惟一線程標(biāo)識(shí)符(可以使用 getId() 獲得該標(biāo)識(shí)符)和一個(gè)新的 Thread.State 類,以及與該類相關(guān)的 getThreadState() 方法。最后一個(gè)線程更改是一個(gè)狀態(tài)枚舉表,該表是用來(lái)監(jiān)視系統(tǒng)狀態(tài),而不是用來(lái)同步狀態(tài)的。

            結(jié)束語(yǔ)

            像添加未捕獲的異常處理程序這樣的簡(jiǎn)單庫(kù)更改,可以極大地增加原代碼的可理解性。雖然在線程組級(jí)別上,新的庫(kù)代碼的功能與原來(lái)庫(kù)代碼的相同,但新模型中的易用性和靈活性遠(yuǎn)遠(yuǎn)超出了將代碼調(diào)整為更新的方式所需的時(shí)間。當(dāng)然,老方法仍然可以使用,但最好將代碼更新為最新的庫(kù)功能。

          ---------------------------------------------------------------------------------------------------------------------------------
          說(shuō)人之短,乃護(hù)己之短。夸己之長(zhǎng),乃忌人之長(zhǎng)。皆由存心不厚,識(shí)量太狹耳。能去此弊,可以進(jìn)德,可以遠(yuǎn)怨。
          http://www.aygfsteel.com/szhswl
          ------------------------------------------------------------------------------------------------------ ----------------- ---------
          posted on 2009-01-16 15:16 宋針還 閱讀(197) 評(píng)論(0)  編輯  收藏 所屬分類: JAVA
          主站蜘蛛池模板: 保德县| 龙江县| 辽宁省| 分宜县| 娄底市| 阿勒泰市| 阿合奇县| 枣庄市| 社旗县| 义马市| 恩平市| 扶风县| 威宁| 西青区| 吕梁市| 长阳| 桐梓县| 通渭县| 喜德县| 商河县| 舒兰市| 香格里拉县| 廊坊市| 越西县| 溧阳市| 墨竹工卡县| 正蓝旗| 宜州市| 廊坊市| 商水县| 和田市| 闽清县| 湘阴县| 崇信县| 广灵县| 怀柔区| 新河县| 孟村| 溧水县| 界首市| 肥东县|