一、线程Thread启动

0. Thread类实现了java.lang.Runnable接口,即实现了run方法。虽然在Sun JDK中,start()调用了start0()方法,start0()方法又是native的,但实际上新的线程就是调用了Thread的run()方法,当然这native的实现中一定有线程的fork操作,使两个线程并列执行。

1. Thread类有8个重载的构造方法。在Sun JDK的源码中,这8个构造方法都是调用了一个私有的init()方法来初始化对象的各个属性。这其中会将当前调用线程作为parent线程,确定线程组和安全权限。说一句,线程组主要是做安全方面考虑的,《Thinking in Java》这本书中表示线程组实际上是一个比较失败的东西,没有具体去提,本文也不会详细去说明。除此之外的代码逻辑如下,设置了各个Thread对象的属性。

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
 
    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        //Determine if it's an applet or not
 
        // If there is a security manager, ask the security manager what to do.
        if (security != null) {
            g = security.getThreadGroup();
        }
 
        //If the security doesn't have a strong opinion of the matter use the parent thread group.
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }
 
    // checkAccess regardless of whether or not threadgroup is explicitly passed in.
    g.checkAccess();
 
    // Do we have the required permissions?
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }
 
    g.addUnstarted();
 
    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    this.name = name.toCharArray();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext = AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    // Stash the specified stack size in case the VM cares
    this.stackSize = stackSize;
 
    // Set thread ID
    tid = nextThreadID();
}

其中target属性是Runnable对象,也是线程所要完成的执行任务内容,而deamon则是一个是否为守护线程的布尔值标记,可以看到默认是继承于当前调用线程的(parent),这个可以通过setDaemon来重新设置,上下文的类加载器contextClassLoader也是一样。

2. 线程的运行启动。即调用start()方法,上面刚刚提到,实际会调用到run()方法。如果是Thread的子类,则可以通过重写run()方法来做任务实现。而在Thread默认的run()方法实现中,是这样子的:

if (target != null) {
target.run();
}

这个target前面提到过,就是初始化的Runnable任务对象。

3. 线程状态。对于一个线程来讲,状态通常可以分为几大类NEW、RUNNING、BLOCKED、DEAD,当然Running可以分为真正执行和就绪,阻塞BLOCKED也有多种阻塞方式。在1.5之后的Sun JDK中,Thread内部是有一个State的枚举类的,但这个通常不是用来做开发,而是用于测试。通常来说,我们能做的就是用Thread对象的isAlive()方法判断,得到true或者false。

二、线程Thread终止

看下面代码,觉得会怎样?

public class App {
static class ExRun implements Runnable {
public void run() {
System.out.println("throwing...");
throw new RuntimeException();
}
} public static void main(String[] args) {
try {
new Thread(new ExRun()).start();
} catch (Exception e) {
System.out.println("runtime ex. catched!");
}
}
}

会在控制台打出”runtime ex. catched!”?你确定么?不怕大家笑话,几年我第一眼看类似这段代码的时候,竟然也认为这很理所当然。其实,这也许不是年头的问题,是是否仔细思考和经验问题。只要仔细想一想,既然已经new了一个Thread并且让他start()去了,异常又不是在new的过程中或者start的过程中出来的,抛不抛异常关这个主线程什么事儿。

那如果一个被主线程创建的新线程除了什么“故障”,就让其悄悄地自生自灭么?有没有补救方案呢?有的。在JavaSE5之前,这个问题直接交给线程组(ThreadGroup)了事儿,但在这之后就有了java.lang.Thread$UncaughtExceptionHandler($是内部关系,后者是前者的内部类/接口)这么一个interface,里面只有一个方法:

void uncaughtException(Thread t, Throwable e);

这个方法就是负责处理异常的,Thread参数不说了,那这个Throwable又是啥,这个就是Exception的老爸了,不了解的异常类层次结构的,这儿您就可以查查去了。既然是Throwable,其实也就不只是Exception了,Error也会被处理到。

接着就简单说下怎么用,Thread类的两个方法:

  • public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
  • public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)

第一个是实例方法,需要针对某个Thread对象使用,而第二个是静态方法,设置全局的默认处理方法,但这两个不冲突,在Thread中是两个独立的handler属性。但毕竟默认的和非默认的还不一样,有个顺序问题,这里又不得不再提到线程组(ThreadGroup)

在JavaSE5之前,没有UncaughtExceptionHandler这个东西,所以出错会直接交给Thread的ThreadGroup对象属性group处理,因为ThreadGroup有处理异常的方法,而UncaughtExceptionHandler出来后,ThreadGroup也实现了这个接口。按照文档注释说明:

  1. 优先检查线程的uncaughtExceptionHandler
  2. 否则交给group处理
  3. 最后才可能轮到defaultUncaughtExceptionHandler

而看源码中线程组的处理实际上是追溯一个parent(也是ThreadGroup类对象)链:

public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}

最后为空时,采用全局默认的的handler。

线程异常就说道这,已经说了不少了,下面简单说两句JVM的退出/停止。

通常我们在课本上都会知道java.lang.System类有个方法是exit(int),这个会让程序退出,而实际上这个方法实际上是java.lang.Runtime类exit(int)的封装(其实System还有像gc()等很多方法,实际执行者都是这个java.lang.Runtime类对象),意义是告诉当前运行环境关闭运行,也就通常被理解为JVM“关闭”。

说下Runtime类,这个类是个单例模式的类,即从头到尾程序只能获得同一个对象引用,即调用静态方法Runtime.getRuntime()所得。Runtime类负责和运行时环境相关的各种任务,有很多有趣的方法,比如很多应用(如Tomcat)中某些线程池中线程数目的确定和当前运行环境处理器个数有关,则可通过Runtime的availableProcessors()方法获得。这里重点说下exit()方法。

还有一个概念需要说下,就是ShutdownHook,这个听起来“高端洋气上档次”的东西实际上就是一个Thread,但它的特殊就在于是一个hook,在JVM关闭(exit)的时候才会执行到。就如线程和中断的关系一样,JVM和exit()也是一样,exit()的执行并不是虚拟机马上关掉,而是安排结束时所要执行任务,按文档说明分上下两个过程,前面的过程就是shutdown对应Thread执行的过程。要想让虚拟机优雅的结束,exit()安排执行shutdownHook清理环境是必要的,还说Tomcat吧,终止的时候也会执行注册过的hook。执行hook的时候,通常守护/非守护线程也都仍然可以执行,但对hookd的新增和移除则不可以了。

Runtime和shutdonwHook关联起来的方法,主要就是这俩了:

  • public void addShutdownHook(Thread hook)
  • public boolean removeShutdownHook(Thread hook)

具体用法不用多说估计看文档都能懂,需要注意的地方就是,想要remove必需在add的时候留有hook的Thread对象引用。

和Thread的stop()类似,Runtime也有halt()方法,但这是一个“暴力停机”的方法,不会考虑hook的执行,如果情况允许,exit()还是优雅的选择。

转自:

线程、Thread类和线程终止相关整理(上)

线程、Thread类和线程终止相关整理(下)——线程异常&JVM停止

【转】线程、Thread类和线程终止的更多相关文章

  1. [深入学习C#]C#实现多线程的方法:线程(Thread类)和线程池(ThreadPool)

    简介 使用线程的主要原因:应用程序中一些操作需要消耗一定的时间,比如对文件.数据库.网络的访问等等,而我们不希望用户一直等待到操作结束,而是在此同时可以进行一些其他的操作.  这就可以使用线程来实现. ...

  2. C#实现多线程的方法:线程(Thread类)和线程池(ThreadPool)

    简介 使用线程的主要原因:应用程序中一些操作需要消耗一定的时间,比如对文件.数据库.网络的访问等等,而我们不希望用户一直等待到操作结束,而是在此同时可以进行一些其他的操作.  这就可以使用线程来实现. ...

  3. net 提供了Thread类用于线程的操作

    net 提供了Thread类用于线程的操作. 当初始化一个线程,把Thread.IsBackground=true的时候,指示该线程为后台线程.后台线程将会随着主线程的推出而退出.后台线程不妨碍程序的 ...

  4. Android(java)学习笔记62:继承Thread类创建线程类

    package cn.itcast_02; /* * 该类要重写run()方法,为什么呢? * 不是类中的所有代码都需要被线程执行的. * 而这个时候,为了区分哪些代码能够被线程执行,java提供了T ...

  5. java基础知识回顾之java Thread类--java线程实现常见的两种方式实现Runnable接口(二)

    创建线程的第二中方式: /** *      步骤: 1定义类实现Runnable接口      2.实现Runnable接口中的run方法.      3.通过Thread类建立线程对象,并将Run ...

  6. 用Thread类创建线程-2

    支持原创,本系列文章均转自:http://www.blogjava.net/nokiaguy/category/38172.html 在Java中创建线程有两种方法:使用Thread类和使用Runna ...

  7. Java多线程01(Thread类、线程创建、线程池)

    Java多线程(Thread类.线程创建.线程池) 第一章 多线程 1.1 多线程介绍 1.1.1 基本概念 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于 ...

  8. 用Thread类创建线程

    在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程, ...

  9. Android(java)学习笔记2:继承Thread类创建线程类

    1. 继承Thread类 创建线程类: package cn.itcast_02; /* * 该类要重写run()方法,为什么呢? * 不是类中的所有代码都需要被线程执行的. * 而这个时候,为了区分 ...

随机推荐

  1. iconv vs mb_convert_encoding

    iconv 字符串按要求的字符编码来转换 string iconv ( string $in_charset , string $out_charset , string $str ) 将字符串 st ...

  2. 20145317彭垚 《Java程序设计》第10周学习总结

    20145317彭垚 <Java程序设计>第10周学习总结 教材学习内容总结 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网 ...

  3. 【php学习】数组操作

    1.json字符串和数组之间的转换 json_decode($jsonStr)      json字符串解码为php变量,若第二个参数默认为false,返回一个对象:若第二个参数设置true,则返回一 ...

  4. 【转】C# HttpWebRequest\HttpWebResponse\WebClient发送请求解析json数据

    http://blog.csdn.net/kingcruel/article/details/44036871 版权声明:本文为博主原创文章,未经博主允许不得转载. ================= ...

  5. Eclipse 安装插件【转】

    本文介绍Eclipse插件的安装方法.Eclipse插件的安装方法大体有三种:直接复制.使用link文件,以及使用eclipse自带的图形界面的插件安装方法. AD: 做为当下最流行的开源IDE之一, ...

  6. sqlserver 通过convert取得指定格式的时间

    http://msdn.microsoft.com/zh-cn/library/ms187928(v=sql.105).aspx CONVERT(NVARCHAR(10),Created,112) 不 ...

  7. html代码转义到js时,往往会遇到问题,这代码实现html和js互转

    这段代码是直接可以用的,大家不妨试试.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...

  8. Python基本数据类型之int 、 float

    首先要明确的是:在python中,一切皆为对象. 从底层角度看,对象就是保存在内存中的一个数据块.从抽象层看,对象就是我们的代码模拟出的一个类的独立个体. 在python中变量不需要声明类型,也不需要 ...

  9. input[type=checkbox]

    一个问题,今天用jquery-1.11.3.min.js时遇到的关于input复选框的问题. 类似于以下代码: <ul class="demo">  <li> ...

  10. Solr4+IKAnalyzer的安装配置

    一.下载Solr4.10.2 我们以Windows版本为例,solr-4.10.2.zip是目前最新版本,下载地址: http://www.apache.org/dyn/closer.cgi/luce ...