JAVA基础知识之多线程——线程组和未处理异常

线程组

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则没有这个问题。

posted @ 2016-11-17 18:07  fysola  阅读(1404)  评论(0编辑  收藏  举报