线程组
Java中的ThreadGroup类表示线程组,在创建新线程时,可以通过构造函数Thread(group...)来指定线程组。
线程组具有以下特征
如果没有显式指定线程组,则新线程属于默认线程组,默认情况下,与创建线程所在的组相同
一旦确定了线程所在线程组之后,不允许更改线程组,直到线程死亡
对于线程组ThreadGroup的一个对象,就表示一个线程组,线程组通过ThreadGroup(group...)来初始化,
线程组可以通过interrput(), setDamemon(),setMaxPriority()等方法来操作组内线程,通过activeCount(),isDamemon()来获取线程信息
下面是一个例子,
1 package threads; 2 3 public class MyThread extends Thread { 4 public MyThread(String name) { 5 super(name); 6 } 7 8 public MyThread(ThreadGroup group, String name) { 9 super(group,name); 10 } 11 12 public void run() { 13 for (int i = 0; i<10; i++) { 14 System.out.println(getName() + " 线程的i变量 " + i); 15 } 16 } 17 }
1 package threads; 2 3 public class ThreadGroupTest { 4 public static void main(String[] args) { 5 //获取主线程所在的线程组,这是所有线程默认的线程组 6 ThreadGroup mainGroup = Thread.currentThread().getThreadGroup(); 7 System.out.println("主线程组的名字: "+mainGroup.getName()); 8 System.out.println("主线程组是否为后台线程组: "+ mainGroup.isDaemon()); 9 new MyThread("主线程组的线程").start(); 10 ThreadGroup tg = new ThreadGroup("新线程组"); 11 tg.setDaemon(true); 12 MyThread tt = new MyThread(tg, "tg线程组的线程甲"); 13 tt.start(); 14 new Thread(tg,"tg线程组的线程乙").start(); 15 } 16 }
未处理异常
JAVA5之后,JVM会在线程抛出未处理异常后自动查找是否有对应的“未处理异常”处理对象,即Thread.UncaughtExceptionHandler对象,如果找到了该处理器对象,就会调用对象的uncaughtException来处理异常。
Thread提供了两个方法来自定义(未处理的)异常处理,
setDefaultUncaughtExceptionHandler(eh), 为线程类的所有实例设置默认的异常处理器
setUncaughtExceptionHandler(eh),为指定线程实例设置异常处理器
而在线程组中,因为ThreadGroup类也实现了Thread.UncaughtExceptionHandler接口,所以线程组会成为默认的异常处理器。
线程组处理异常的流程如下,
- 如果该线程组有父线程组,则调用父线程组的uncaughtException方法来处理异常
- 如果线程类有默认异常处理器,则用该异常处理器处理异常
- 如果异常对象不是ThreadDeath,就会打印异常信息,并结束该线程
下面演示一个例子,为主线程设置一个默认的异常处理器,当主程序抛出一个未处理异常时,该异常处理器将会起作用。
当一个线程抛出一个未处理异常时,JVM首先会查找该异常对应的异常处理器,
1 package threads; 2 3 //自定义异常处理器,只需要实现Thread.UncaughtExceptionHandler接口 4 public class MyExHandler implements Thread.UncaughtExceptionHandler { 5 6 //uncaughtException方法将处理线程中未处理的异常 7 @Override 8 public void uncaughtException(Thread t, Throwable e) { 9 // TODO Auto-generated method stub 10 System.out.println("自定义异常处理: "+ t + " 线程出现了异常 " + e); 11 } 12 }
1 package threads; 2 3 public class ExHandler { 4 public static void main(String[] args) { 5 //设置主线程的默认异常处理器 6 Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler()); 7 int a = 5/0; 8 System.out.println("程序正常结束"); 9 } 10 }
执行结果,可以看到程序虽然用自定义的异常处理器处理了一个未处理的异常,但还是没有正常结束程序,而是在处理完异常后直接结束了程序。
1 自定义异常处理: Thread[main,5,main] 线程出现了异常 java.lang.ArithmeticException: divide by zero
如果注释掉主程序中的第6行,让JVM来处理未处理的异常,执行结果如下,也是不能正常结束程序
1 Exception in thread "main" java.lang.ArithmeticException: divide by zero 2 at threads.ExHandler.main(ExHandler.java:7)
如果使用try catch finally来处理, 修改主程序如下,
1 package threads; 2 3 public class ExHandler { 4 public static void main(String[] args) { 5 //设置主线程的默认异常处理器 6 //Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler()); 7 try { 8 int a = 5/0; 9 } catch (ArithmeticException e) { 10 e.printStackTrace(); 11 } finally { 12 System.out.println("程序正常结束"); 13 } 14 } 15 }
执行结果如下,可以看到这时候程序可以正常结束了,说明用try catch处理异常时,异常不会向上传给调用者
1 java.lang.ArithmeticException: divide by zero程序正常结束 2 3 at threads.ExHandler.main(ExHandler.java:8)
以下引用自 阿里巴巴Java开发手册
9. 【强制】多线程并行处理定时任务时,Timer运行多个TimeTask 时,只要其中之一没有捕获
抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。