import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Set; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args){ // CaptureThreadException.test(); // EmptyException.test(); // ThreadHook.test(); PreventDuplicated.test(); } } /* 7.1 獲取線程運行時異常 public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) :爲某個線程指定UncaughtExceptionHandler public UncaughtExceptionHandler getUncaughtExceptionHandler(); :獲取某個線程的UncaughtExceptionHandler public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) :設置全局的UncaughtExceptionHandler public static void setDefaultUncaughtExceptionHandler(UncaughtException eh) :獲取全局的UncaughtExceptionHandler 函數接口,該回調接口會被Thread中的dispatchUncaughtExcept方法調用,當線程在運行 的過程當中出現了異常時,JVM會調用dispatchUncaughtExcept方法,該方法將對應的線程實 例以及異常信息傳遞給回調接口: public interface UncaughtExceptionHandler{ void uncaughtException(Thread t, Throwable e); } private void dispatchUncaughtExcept(Throwable e){ getUncaughtExceptionHandler().uncaughtException(this.e); } 閒談: 在平時的工做中,這種設計方式是比較常見的,尤爲是那種異步執行方法,不如Google的 guava toolkit就提供了EventBus,在EventBus中事件源和實踐的subscriber二者 藉助於EventBus實現了徹底的解耦合,可是在subscriber執行任務時有可能會出現異常 狀況,EventBus一樣也是藉助一個ExceptionHandler進行回調處理的。 */ class CaptureThreadException{ public static void test() { Thread.setDefaultUncaughtExceptionHandler((t,e)->{ System.out.println(t.getName()+" occur exception"); e.printStackTrace(); }); final Thread t = new Thread(()->{ try{ TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { } System.out.println(1/0); },"Test-Thread"); t.start(); } } /* 7.1.3 UncaughtExceptionHandler源碼分析 未注入UncaughtExceptionHandler回調接口的狀況下,就從ThreadGroup中獲取: 1.該ThreadGroup若是有父ThreadGroup,則直接使用父Group的UncaughtException 2.若是設置了全局默認的UncaughtExceptionHandler,則調用UncaughtException 3.既沒有父,又沒有全局的,則直接將異常的堆棧信息定向到System.err中 案例分析: 因爲沒有指定,因此尋找過程爲: 線程出現異常=>Main Group=>System Group=>System.err */ class EmptyException{ public static void test() { ThreadGroup main = Thread.currentThread().getThreadGroup(); System.out.println(main.getName()); System.out.println(main.getParent()); System.out.println(main.getParent().getParent()); final Thread thread = new Thread(()->{ try{ TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(1/0); },"Test-Thread"); thread.start(); } } /* 7.2.1 Hook線程介紹 Jvm進程的退出是因爲JVM進程沒有活躍的非守護線程,或者系統中斷了信號。向JVM程序 注入多個Hook線程,在JVM進程退出的時候,Hook線程會啓動執行,經過Runtime能夠爲 JVM注入多個Hook線程。 */ class ThreadHook{ public static void test() { Runtime.getRuntime().addShutdownHook(new Thread(){ public void run(){ try{ System.out.println("The hook thread 1 is running..."); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("The hook thread 1 will exit."); } }); Runtime.getRuntime().addShutdownHook(new Thread(){ public void run(){ try{ System.out.println("The hook thread 2 is running..."); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("The hook thread 2 will exit."); } }); } } /* 7.2.2 Hook線程實戰 需求: 在咱們開發中常常會遇到Hook線程,好比爲了防止某個程序被重複啓動,在進行啓動時 會建立一個lock文件,進程收到中斷信號的時候會刪除這個lock文件,咱們在mysql服 務器、zookeeper、kafka等系統中都能看到lock文件的存在,本節,將利用hook線程 的特色,模擬一個防止重複啓動的線程。 */ class PreventDuplicated { private final static String LOCK_PATH = "C:\\Users\\Administrator\\Desktop\\JavaAPI"; private final static String LOCK_FILE = ".lock"; private final static String PERMISSIONS = "rw-------"; private static Path getLockFile(){ return Paths.get(LOCK_PATH,LOCK_FILE); } private static void checkRunning(){ Path path = getLockFile(); if(path.toFile().exists()){ throw new RuntimeException("The program already running..."); } /* 很尷尬一點,這個地方貌似是針對Linux的寫法,我這個地方出現了問題, 暫時先將這個問題放一下吧。。。 */ Set<PosixFilePermission> perms = PosixFilePermissions.fromString(PERMISSIONS); try { Files.createFile(path,PosixFilePermissions.asFileAttribute(perms)); } catch (IOException e) { e.printStackTrace(); } } public static void test(){ Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run() { System.out.println("The program received kill SIGNAL."); getLockFile().toFile().delete(); } }); checkRunning(); while (true) { try{ TimeUnit.MILLISECONDS.sleep(1); System.out.println("program is running..."); } catch (InterruptedException e) { e.printStackTrace(); } } } } /* 7.2.3 Hook線程應用場景及注意事項 1.Hook線程只有在收到退出信號的時候會被執行,若是kill使用了-9,那麼 Hook線程不會執行,所以lock文件也不會被清理 2.Hook線程中也能夠執行一些資源的釋放工做,如關閉文件句柄、socket連接 、數據庫connection等 */
《Java高併發編程詳解》筆記java