1.4 线程属性
在下面一节中,我们将讨论线程的各种属性:线程优先级、守护线程、线程组,以及处理未捕获异常的处理器。
1.4.1 线程优先级
在Java程序设计语言中,每一个线程都有一个优先级。默认情况下,一个线程继承他父线程的优先级。一个线程的父线程就是启动它的那个线程。你可以通过setPriority方法提高或降低任何一个线程的优先级,你可以将优先级设置为在MIN_PRIORITY(在Thread类中定义为1)和MAX_PRIORITY(定义为10)之间的任何值。NORM_PRIORITY被定义为5。
无论何时,当线程调用器有机会去挑选一个新的线程时,他会优先考虑高优先级的线程。但是,线程优先级是高度依赖系统的。当虚拟机依赖于宿主机平台的线程实现机制时,Java线程优先级就会被映射成宿主机平台上的优先级,宿主机平台的优先级级别在数量上可能和Java里的有些出入。
例如,Windows NT/XP有7个优先级级别。Java的某些优先级将会映射到相同的操作系统优先级上。在sun为Linux提供的虚拟机中,线程优先级完全被忽略,所有线程都具有相同的优先级。
因此,最好将线程优先级看作线程调度器的一个参考因素。千万不要将程序构建为其功能的正确性依赖于优先级。
警告:如果你确实要使用优先级,你应该警惕初学者常犯的一个错误。urguo你有一些高优先级的线程很少发生阻塞,那么低优先级的线程可能就永远都运行不了。无论何时,当调度器决定挑选一个新的线程的时候,它都会首先在优先级高的线程中进行选择,即使这样会使低优先级的线程完全饿死也是如此。
API java.lang.Thread 1.0
在下面一节中,我们将讨论线程的各种属性:线程优先级、守护线程、线程组,以及处理未捕获异常的处理器。
1.4.1 线程优先级
在Java程序设计语言中,每一个线程都有一个优先级。默认情况下,一个线程继承他父线程的优先级。一个线程的父线程就是启动它的那个线程。你可以通过setPriority方法提高或降低任何一个线程的优先级,你可以将优先级设置为在MIN_PRIORITY(在Thread类中定义为1)和MAX_PRIORITY(定义为10)之间的任何值。NORM_PRIORITY被定义为5。
无论何时,当线程调用器有机会去挑选一个新的线程时,他会优先考虑高优先级的线程。但是,线程优先级是高度依赖系统的。当虚拟机依赖于宿主机平台的线程实现机制时,Java线程优先级就会被映射成宿主机平台上的优先级,宿主机平台的优先级级别在数量上可能和Java里的有些出入。
例如,Windows NT/XP有7个优先级级别。Java的某些优先级将会映射到相同的操作系统优先级上。在sun为Linux提供的虚拟机中,线程优先级完全被忽略,所有线程都具有相同的优先级。
因此,最好将线程优先级看作线程调度器的一个参考因素。千万不要将程序构建为其功能的正确性依赖于优先级。
警告:如果你确实要使用优先级,你应该警惕初学者常犯的一个错误。urguo你有一些高优先级的线程很少发生阻塞,那么低优先级的线程可能就永远都运行不了。无论何时,当调度器决定挑选一个新的线程的时候,它都会首先在优先级高的线程中进行选择,即使这样会使低优先级的线程完全饿死也是如此。
API java.lang.Thread 1.0
- void setPriority(int newPriority)设置线程的优先级。优先级必须在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间。一般情况下,使用Thread.NORM_PRIORITY优先级。
- static int MIN_PRIORITY 线程的最低优先级,值为1。
- static int MAX_PRIORITY 线程的最高优先级,值为10。
- static int NORM_PRIORITY 线程的默认优先级,值为5。
- static void yield() 导致当前执行线程进入让步状态。如果有优先级不低于他的其他可运行线程存在,那么这些线程接下来就会被调度。请注意,这是一个静态方法。
1.4.2 守护线程
你可以通过调用
t.setDaemon(true);
将线程转变成一个守护线程。这样的线程并没有什么神奇的地方。一个守护线程的唯一作用就是为其他线程提供服务。计时器线程就是一个例子,他定是发送“时间嘀嗒”信号给其他线程。当只剩下守护线程的时,虚拟机就退出了,因为如果剩下的线程都是守护线程,就没有继续运行程序的必要了。
API java.lang.Thread 1.0
你可以通过调用
t.setDaemon(true);
将线程转变成一个守护线程。这样的线程并没有什么神奇的地方。一个守护线程的唯一作用就是为其他线程提供服务。计时器线程就是一个例子,他定是发送“时间嘀嗒”信号给其他线程。当只剩下守护线程的时,虚拟机就退出了,因为如果剩下的线程都是守护线程,就没有继续运行程序的必要了。
API java.lang.Thread 1.0
- void setDaemon(boolean isDaemon) 将线程标记为守护线程或用户线程。这个方法必须在线程开始执行之前调用。
- 1.4.3 线程租
某些程序只有很少的线程。如果将它们按照功能归类将很有用。以一个Internet浏览器为例。如果大量线程正试图从服务器上获取图片,此时用户点击Stop按钮来中断当前页面的载入,那么应该有很方便的方法同时中断所有这些线程。Java程序设计语言允许你创建线程租,这样就可以同时对一组线程进行操作。
你可以用下面的构造器来创建线程租:
String groupName = ...;
ThreadGroup g = new ThreadGroup(groupName);
ThreadGroup构造器中的字符串参数是用来表示该组的,它必须是唯一的。然后你可以添加线程到这个组中,方法是在线程的构造器中指定线程组。
Thread t = new Thread(g,threadName);
要查明某个特定线程组中是否有线程仍然处于可运行状态,应该使用activeCount方法。
if(g.activeCount == 0)
{
// all threads in the group g have stopped.
}
要中断一个线程组中所有线程,只需要在组对象上调用interrupt方法
g.interrupt(); //interrupt all threads in group g
但是,executor可以使你在不使用线程组的情况下完成相同的任务。[关注:线程和Swing]
线程组可以有子线程组。默认情况下,一个新建的线程组是当前线程组的子线程组。但是,你可以在构造器中明确地给出父线程组的名字(见API说明)。像activeCount和interrupt这样的方法引用的是本组和所有子组中的线程。
API java.lang.Thread 1.0
- Thread(ThreadGroup g, String name) 创建一个属于给定ThreadGroup的新Thread。参数:g —— 新线程所属的线程组;name —— 新线程的名字
- ThreadGroup getThreadGroup() 返回该线程所在的线程组。
- ThreadGroup(String name) 创建一个新的ThreadGroup。它的父线程组是当前线程所在的组。参数:name——新线程组的名字;;
- ThreadGroup(ThreadGroup parent, String name) 创建一个新的ThreadGroup。参数:parent——新线程组所属的父线程组;name——新线程组的名字
- int activeCount()返回线程组中活线程数的上界;
- int enumerate(Thread[] list) 得到线程组中所有活线程的引用。你可以使用activeCount方法得到这个数组的上界;这个方法返回放入数组的线程数量。如果数组长度太短(假设因为调用activeCount之后又产生了更多的线程),那么就会插入适量的线程。参数:list——被填入线程引用的数组;
- ThreadGroup getParent() 得到这个线程组的父线程组;
- void interrupt() 中断这个线程组和他的子线程组中的所有线程。
1.4.4 未捕获异常处理器
线程的run方法不能抛出任何被检查的异常,但是不被检查的异常可以导致线程终止。如果出现这种情况,线程就死亡了。
然而,并不需要任何catch子句来处理可以被传播的异常。在线程死亡前,异常将被传地给未捕获异常处理器来处理。
处理器必须从属于实现了Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法。
如果你没有安装默认处理器,那么默认的处理器就是null。但是,如果你没有为一个单独的线程安装处理器,此时的处理器就是线程的ThreadGroup对象。
ThreadGroup类实现了Thread.UncaughtExceptionHandler接口。它的uncaughtException方法作如下操作:
1)如果该线程组有父线程组,则调用父线程组的uncaughtException方法。
2)否则,如果Thread.getDefaultExceptionHandler方法返回一个非null的处理器,则调用该处理器。
3)否则,如果Throwable是一个ThreadDeath实例,则什么都不做;
4)否则,线程的名字和Throwable的堆栈总计将被输出到System.err上。
堆栈踪迹你肯定在自己的程序中多次看到过。
注意:在JDK5.0之前,你不能为每个线程都安装一个未捕获异常处理器,你也不能指定默认处理器。想要安装一个处理器,你需要继承ThreadGroup类,并重载uncaughtException方法。
API java.lang.Thread 1.0
API java.lang.Thread.UncaughtExceptionHandler 5.0
线程的run方法不能抛出任何被检查的异常,但是不被检查的异常可以导致线程终止。如果出现这种情况,线程就死亡了。
然而,并不需要任何catch子句来处理可以被传播的异常。在线程死亡前,异常将被传地给未捕获异常处理器来处理。
处理器必须从属于实现了Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法。
- void uncaughtException(Thread t, Throwable e)
如果你没有安装默认处理器,那么默认的处理器就是null。但是,如果你没有为一个单独的线程安装处理器,此时的处理器就是线程的ThreadGroup对象。
ThreadGroup类实现了Thread.UncaughtExceptionHandler接口。它的uncaughtException方法作如下操作:
1)如果该线程组有父线程组,则调用父线程组的uncaughtException方法。
2)否则,如果Thread.getDefaultExceptionHandler方法返回一个非null的处理器,则调用该处理器。
3)否则,如果Throwable是一个ThreadDeath实例,则什么都不做;
4)否则,线程的名字和Throwable的堆栈总计将被输出到System.err上。
堆栈踪迹你肯定在自己的程序中多次看到过。
注意:在JDK5.0之前,你不能为每个线程都安装一个未捕获异常处理器,你也不能指定默认处理器。想要安装一个处理器,你需要继承ThreadGroup类,并重载uncaughtException方法。
API java.lang.Thread 1.0
- static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler) 5.0
- static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() 5.0
- void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler) 5.0
- Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() 5.0
API java.lang.Thread.UncaughtExceptionHandler 5.0
- void uncaunghtException(Thread t, Throwable e) 定义该方法是为了在线程因为一个未捕获异常而终止时,将客户报告记录到日志中。参数:t——因为一个未捕获异常而终止的线程;e——未捕获的异常对象。
- void uncaughtException(Thread t, Throwable e)