博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程组和未处理的异常
阅读量:4584 次
发布时间:2019-06-09

本文共 4278 字,大约阅读时间需要 14 分钟。

Java 使用 ThreadGroup 来表示线程组,它可以对一批线程进行分类管理,Java 允许程序直接对线程组进行控制。对线程组的控制相当于同时控制这批线程。用户创建的所有线程都属于指定线程组,如果程序没有显式指定线程属于哪个线程组,则该线程属于默认线程组。在默认情况下,子线程和创建它的父线程处于同一个线程组内,例如A线程创建了B线程,并且没有指定B线程的线程组,则B线程属于A线程所在的线程组。

一旦某个线程加入了指定线程组之后,该线程将一直属于该线程组,直到该线程死亡,线程运行中途不能改变它所属的线程组。

Thread 类提供了如下几个构造器来设置新创建的线程属于哪个线程组。

1)Thread(ThreadGroup group, Runnable target):以 target 的 run() 方法作为线程执行体创建新线程,属于group线程组。

2)Thread(ThreadGroup group, Runnable target, String name):以 target 的 run() 方法作为线程执行体创建新线程,该线程属于group线程组,且线程名为name。

3)Thread(ThreadGroup group, String name):创建新线程,新线程名为 name,属于 group 线程组。

因为中途不可改变线程所属的线程组,所以Thread类没有提供setThreadGroup() 方法来改变线程所属的线程组(但提供了一个 getThreadGroup() 方法来返回该线程所属的线程组,getThreadGroup() 方法的返回值是ThreadGroup对象,表示一个线程组。ThreadGroup 类提供了如下两个简单的构造器来创建实例。

1)ThreadGroup(String name):以指定的线程组名字来创建新的线程组。

2)ThreadGroup(ThreadGroup parent, String name):以指定的名字、指定的父线程组创建一个新线程组。

上面两个构造器在创建线程组实例时都必须为指定一个名字。也就是说,线程组总会具有一个字符串类型的名字,该名字可通过调用ThreadGroup的getName() 方法来获取,但不允许改变线程组的名字。ThreadGroup类提供了如下几个常用的方法来操作整个线程组的所有线程。

1)int activeCount():返回此线程组中活动线程的数目。

2)interrupt():中断此线程组中所有的线程。

3)isDaemon():判断该线程组是否是后台线程组。

4)setDaemon(boolean daemon):把该线程组设置成后台线程组。后台线程组具有一个特征——当后台线程组的最后一个线程执行结束或最后一个线程被销毁后,后台线程组将自动销毁。

5)setMaxPriority(int pri):设置线程组的最高优先级。

下面程序创建几个线程,它们分别属于不同的线程组,程序还将一个线程组设置成后台线程。

class TestThread extends Thread{    //提供指定线程名的构造器    public TestThread(String name){        super(name);    }    //提供指定线程名、线程组的构造器    public TestThread(ThreadGroup group , String name){        super(group, name);    }    public void run(){        for (int i = 0; i < 20 ; i++ ){            System.out.println(getName() + " 线程的i变量" + i);        }    }}public class ThreadGroupTest{    public static void main(String[] args) {        //获取主线程所在的线程组,这是所有线程默认的线程组        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();       System.out.println("主线程组的名字:" + mainGroup.getName());       System.out.println("主线程组是否是后台线程组:" + mainGroup.isDaemon());       new TestThread("主线程组的线程").start();       ThreadGroup tg = new ThreadGroup("新线程组");       tg.setDaemon(true);       System.out.println("tg线程组是否是后台线程组:" + tg.isDaemon());       TestThread tt = new TestThread(tg , "tg组的线程甲");       tt.start();       new TestThread(tg , "tg组的线程乙").start();    }}

上面程序中的第一段粗体字代码用于获取主线程所属的线程组,并访问该线程组的相关属性,第二段粗体字代码创建了一个新线程组,并将该线程组设置成后台线程。

ThreadGroup 内还定义了一个很有用的方法:void uncaughtException(Thread t , Throwable e),该方法可以处理该线程组内任意线程所抛出的未处理异常。

从 Java 5 开始, Java 加强了线程的异常处理,如果线程执行过程中抛出了一个未处理异常, JVM 在结束该线程之前会自动查找是否有对应的 Thread.UncaughtExceptionHandler 对象,如果找到该处理器对象,则会调用该对象的 uncaughtException(Thread t, Throwable e) 方法来处理该异常。

Thread.UncaughtExceptionHandler 是 Thread 类的一个静态内部接口,该接口内只有一个方法: void uncaughtException(Thread t , Throwable e),该方法中的 t 代表出现异常的线程,而 e 代表该线程抛出的异常。

Thread 类提供了如下两个方法来设置异常处理器。

1) static setDefaultUncaughtExceptionHandler (Thread.UncaughtExceptionHandler eh):为该线程类的所有线程实例设置默认的异常处理器。

2) setUncaughtExceptionHandler (Thread.UncaughtExceptionHandler  eh):为指定的线程实例设置异常处理器。

ThreadGroup 类实现了 Thread.UncaughtExceptionHandler 接口,所以每个线程所属的线程组将会作为默认的异常处理器。当一个线程抛出未处理异常时, JVM 会首先查找该异常对应的异常处理器(setUncaughtExceptionHandler() 方法设置的异常处理器),如果找到该异常处理器,则将调用该异常处理器处理该异常;否则,JVM 将会调用该线程所属的线程组对象的 uncaughtException() 方法来处理该异常。

线程组处理异常的默认流程如下。

1)如果该线程组有父线程组,则调用父线程组的 uncaughtException() 方法来处理该异常。

2)如果该线程实例所属的线程类有默认的异常处理器(由 setDefaultUncaughtExceptionHandler() 方法设置的异常处理器),那么就调用该异常处理器来处理该异常。

3)如果该异常对象是 ThreadDeath 的对象,则不做任何处理;否则,将异常跟踪栈的信息打印到 System.err 错误输出流,并结束该线程。

下面程序为主线程设置了异常处理器,当主线程运行抛出未处理异常时,该异常处理器将会起作用。

class MyExHandler implements Thread.UncaughtExceptionHandler{    //实现uncaughtException方法,该方法将处理线程的未处理异常    public void uncaughtException(Thread t, Throwable e){        System.out.println(t + " 线程出现了异常:" + e);    } }public class ExHandler{    public static void main(String[] args){        //设置主线程的异常处理器       Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler());       int a = 5 / 0; //①     System.out.println("程序正常结束!");   } }

上面程序的主方法中粗体字代码为主线程设置了异常处理器,而①号代码处将引发一个未处理异常,则该异常处理器会负责处理该异常。运行该程序,会看到如下输出:

Thread [main, 5, main]线程出现了异常:java.lang.ArithmeticException: / by zero

从上面程序的执行结果来看,虽然程序中粗体字代码指定了异常处理器对未捕获的异常进行处理,而且该异常处理器也确实起作用了,但程序依然不会正常结束。这说明异常处理器与通过 catch 捕获异常是不同的——当使用 catch 捕获异常时,异常不会向上传播给上一级调用者;但使用异常处理器对异常进行处理之后,异常依然会传播给上一级调用者。

转载于:https://www.cnblogs.com/jwen1994/p/10632372.html

你可能感兴趣的文章
MySQL性能优化的21条最佳经验【转】
查看>>
关于udo3d双目相机的嵌入式板子系统重装
查看>>
nginx安装缺少依赖记录
查看>>
vcs编译verilog/sysverilog并执行
查看>>
Mvc 提交表单的4种方法全程详解
查看>>
质因数分解
查看>>
2015年4月30日
查看>>
《Intel汇编第5版》 条件汇编伪指令
查看>>
mybatis如何根据mapper接口生成其实现类(springboot)
查看>>
K8S集群技术
查看>>
Failed to load resource: the server responded with a status of 404 (Not Found) favicon.ico文件找不到...
查看>>
Linux基础命令小结
查看>>
黑马程序员--抽象类与接口
查看>>
IaaS,PaaS,SaaS 的区别
查看>>
Python复习基础篇
查看>>
关于Cocos2d-x中背景音乐和音效的添加
查看>>
.Net持续集成 —— Jenkins+Git+WebDeploy
查看>>
01_Numpy基本使用
查看>>
吴裕雄--天生自然 R语言开发学习:使用键盘、带分隔符的文本文件输入数据
查看>>
CSS选择器详解
查看>>