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

接着上一篇,这章开始更详细地整理并发相关的内容,本篇主要说说线程概念、Thread类和线程的终止

说并发就不得不说说进程线程,这也是为后续的文章整理奠定基础或者说做个开端。在操作系统层面来看,任务的完成实际上是要运行一道程序,而进程则可以理解为是运行中的一个程序的实例,进程也是系统资源分配的一个单元。对进程的描述,在Linux内核源码层面看,就是一个task_struct的结构体实例。但为了更好的调度和运行,往往一个进程又包含一个或多个线程,线程不作为系统资源分配的单元,往往共享这些资源,而只是一个调度的单元,有自己的局部变量、运行栈和程序计数器等。这在Linux中也是一个task_struct,但指向的资源通常是公共的,而其他属性也有特定的设置。

对于Java,在最初就支持了线程,Java线程对应于java.lang.Thread类对象。通常一个运行JVM就是一个线程,而Java线程和操作系统的线程有多种可能的对应关系,这要以支持的平台为依据。如Linux下,一个Java线程通常对应于一个操作系统的线程。下面我们就来看看java.lang.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还有像ThreadLocal和优先级等这样的属性,此文不对这些一一细说,至于ThreadLocal,看后面有没有机会整理下。Thread的方法很多需要底层支持,很多都是native实现。另外,Thread除了有很对对象的方法外,还有很多实用的静态方法,如currentThread()等。

鉴于到此此篇文章已不短,先标记个“(上)”,线程终止下篇详说。

此条目发表在 Java, Java语言, 并发, 开发, 计算机技术 分类目录,贴了 , , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>