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