4.1线程组(Thread Group)

xiaoxiao2021-02-27  140

在我们研究Thread类的时候,你可能会遇到涉及java.lang,ThreadGroup类的构造器,像Thread(ThreadGroup group,Runnable target),和方法如static int activeCount()和static int enumerate(Thread[] tarray).   JDK文档对于ThreadGroup是这样介绍的,一个线程组“代表一个线程集合。此外,一个线程组可以包含其它线程组。这个线程组从一棵树而来,每一个线程组希望初始线程组拥有一个父线程。”   运用一个ThreadGroup对象,你可以在所有包含Thread对象中实现一个操作。例如,通过变量tg,tg.suspednd()明确一个线程组的引用;在一个线程组内暂停所有线程。线程组简单地控制着许多线程。 尽管ThreadGroup显现出来是非常有用,但是你需要尽量避免这个类,有如下原因:      (1)更有用的ThreadGroup方法是void suspend()、void resum()和void stop()。这些方法不赞成使用,因为像它们的线程副本(每一个方法代表着每一个线程在线程组中),它们容易发生死锁和其它问题。       (2)ThreadGroup是线程不安全的。例如,为了在一个线程组包含着一个数量的活跃线程,你需要请求ThreadGroup的int activeCount()方法。你可能运用这个方法去定义数组的大小,这样你就可以调用ThreadGroup的其中一个方法enumerate()。然而,这样就不能保证这个计算是精确的,因为在你创建数组和调用enumerate()方法的时间之间,这个计数可能会因为其它线程的创建或暂停而改变。如果这个数组太小,enumerate()将会忽略额外的线程。与我们说的Thread的activeCount()和enumerate()的方法一样,它代表着当前线程的ThreadGroup方法。“时间查检用于时间(time of check to time of use)“类的软件Bug是一个问题例子(读者可以访问:https://en.wikipedia.org/wiki/Time_of_ check_to_time_of_use)。(这个bug也出现在糟糕脚本线程中,在应用操作一个文件之前,需要去查检文件是否存在。在查检和操作文件之间,这个文件可能被删除或创建。)     然而,你仍然需要了解ThreadGroup,因为它在线程运行期间控制其抛出异常有作用。Listing4-1的例子了解在出现run()方法内试图让除数为0的应用,而执行异常的操作,这个结果会抛出java.lang.ArithmeticException对象。

    Listing4-1 从方法run()方法中抛出异常。

package com.owen.thread.chapter4; public class ExceptionThread { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { int x = 1 / 0; // Line 10 } }; Thread thd = new Thread(r); thd.start(); } }     在默认主线程创建一个runnable,我们故意让整数除以0然后抛出一个ArithmeticException对象。     执行这段代码,你将会看到如下的异常:

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at com.owen.thread.chapter4.ExceptionThread$1.run(ExceptionThread.java:13) at java.lang.Thread.run(Thread.java:745)   当一个异常从run()方法抛出时,这个线程将会结束,并且下面的活动将会取代它:        (1)Java虚拟机(JVM)寻找一个实例的Thread。通过Thread的void setUncaughtException(Thread.UncaughtException eh)方法实例一个Thread.UncaughtExceptionHandler。当这个实例被找到之后,它将会通过执行实例的void uncaughtException(Thread t, Throwable e)方法,去抛出异常或错误——可能是一个java.lang.outOfMemoryError对象抛出;方法中的t明确Thread对象,和e明确抛出异常或错误。如果uncaughtException()抛出一个异常或错误,这个异常或错误将会被java虚拟机忽略。       (2)假设setUncaughtExceptionHandler()不会被请求给任命的操控,那么Java虚拟机(JVM)通过控制着连接ThreadGroup对象uncaughtExeption(Thread t, Throwable r)方法。假设ThreadGroup不被扩展和它的uncaughtException()方法不能继承和uncaughtException()方法不能重载去操作异常;当父级的ThreadGroup存在时。uncaughtException通过控制父级的ThreadGroup对象的uncaughtException()方法。否则,线程将会去检查是否有其它的默认不能捕抓异常出现(通过Thread的static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler)方法)。如果默认不能捕抓的异常存在,它的uncaughtException()方法将被两个相同的参数请求。否则,uncaughtException()检查它的Throwable参数去确定是否它的一个java.lang.ThreadDeath存在。如果存在,不会做什么特殊事。另外,如Listing4-1期待的信息展示,一个信息包含一个线程的名称,和一个栈的撤消,运用Throwable参数的printStackTrace()方法,它将会打印出标准的错误流。     Listing4-2证明Thread的setUncaughtExceptionHandler()和setDefaultUncaughtExceptionHandler()方法。      Listing4-2证明不能捕抓异常操作。

package com.owen.thread.chapter4; public class ExceptionThread4_2 { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { int x = 1 / 0; } }; Thread thd = new Thread(r); Thread.UncaughtExceptionHandler uceh; uceh = new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("Caught throwable " + e + " for thread " + t); } }; thd.setUncaughtExceptionHandler(uceh); uceh = new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("Default uncaught exception handler"); System.out.println("Caught throwable " + e + " for thread " + t); } }; thd.setDefaultUncaughtExceptionHandler(uceh); thd.start(); } }    执行这个代码,你将得到如下的异常: Caught throwable java.lang.ArithmeticException: / by zero for thread Thread[Thread-0,5,main]    你不能看到默认非捕抓异常操作输出信息,因为默认操作不会被请求。为了看到默认异常输出,你必须注释掉thd.setUncaughtExceptionHandler(uche);.如果你也注释掉了thd.setDefaultUncaughtExceptionHandler(uceh);,那么你将会看Listing4-1的输出信息。

源码下载:git@github.com:owenwilliam/Thread.git

转载请注明原文地址: https://www.6miu.com/read-16145.html

最新回复(0)