Java线程Thread类详解

Thread类是Java中常见的一个类,本文及后面几篇文章详细分析该类是如何实现的。

成员变量

Thread类中与成员变量有关的代码如下所示:

public class Thread implements Runnable {
    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;

    /* Whether or not to single_step this thread. */
    private boolean     single_step;

    /* Whether or not the thread is a daemon thread. */
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    /* What will be run. */
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * The requested stack size for this thread, or 0 if the creator did
     * not specify a stack size.  It is up to the VM to do whatever it
     * likes with this number; some VMs will ignore it.
     */
    private long stackSize;

    /*
     * JVM-private state that persists after native thread termination.
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID
     */
    private long tid;

    /* For generating thread ID */
    private static long threadSeqNumber;

    /* Java thread status for tools,
     * initialized to indicate thread 'not yet started'
     */

    private volatile int threadStatus = 0;


    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();
    /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
     */
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }
}

根据变量名称和英文注释,各成员变量的作用如下表:

成员变量名 变量类型 变量含义
name String 线程名称
priority int 线程优先级
eetop long JVM中的JavaThread指针
daemon boolean 是否是守护线程,默认false
target Runnable 用来引用构造函数中传递的Runnable参数,表示线程执行的代码
group ThreadGroup 线程所属的线程组
contextClassLoader ClassLoader 线程用的上下文类加载器,该上下文类加载器可供线程加载类和资源
threadInitNumber int 计数变量,用在nextThreadNum方法中为匿名线程生成名称
threadLocals ThreadLocal.ThreadLocalMap 与线程局部变量相关
inheritableThreadLocals ThreadLocal.ThreadLocalMap 与线程局部变量相关
stackSize int 线程栈大小
tid long 线程ID
threadSeqNumber long 计数变量,用在nextThreadID方法中为线程生成ID
threadStatus int 线程状态
parkBlocker Object 用于LockSupport.park(Object)方法中,用于引用该方法参数
blocker Interruptible 线程在执行可中断IO操作时阻塞该线程的对象,线程的中断状态被设置后blocker的interrupt方法会被调用
blockerLock Object 设置上述blocker变量时用的锁

构造函数

Thread类有8个公有的构造函数和1个包私有的构造函数,代码如下所示:

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

Thread(Runnable target, AccessControlContext acc) {
    init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}

public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}

public Thread(String name) {
    init(null, null, name, 0);
}

public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}

public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}

public Thread(ThreadGroup group, Runnable target, String name) {
    init(group, target, name, 0);
}

public Thread(ThreadGroup group, Runnable target, String name,
                long stackSize) {
    init(group, target, name, stackSize);
}

在这些构造函数中,若参数没有名称,那么便会用nextThreadNum方法为新的线程生成一个“Thread-”加计数的名称。另外这些构造函数在内部都调用了init方法,下面来看一下init方法:

private void init(ThreadGroup g, Runnable target, String name,
                    long stackSize) {
    init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
                    long stackSize, AccessControlContext acc,
                    boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    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();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && 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();
}
  • 线程名不可为null;
  • 如果没有显式设置线程组,那么首先从SecurityManager获得线程组,若仍然没有,那么则设置成父线程(即创建该线程的线程)所属的线程组;
  • 是否为守护线程、线程优先级都与父线程相同;
  • 如果继承父线程的ThreadLocal,那么将父线程的inheritableThreadLocals拷贝给该线程的inheritableThreadLocals变量;
  • 自动为该线程生成ID。

成员方法

成员变量的get和set方法在此不再赘述,重点看下面几个方法。

start方法

start方法执行后,JVM会调用该线程的run方法。在run方法中,如果构造函数传递了不是null的Runable对象,则执行Runnable对象的run方法,否则什么也不做。

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
                it will be passed up the call stack */
        }
    }
}

private native void start0();

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}
  • 首先检查线程状态,如果不是初始值0则抛出异常,线程状态值见下文getState方法一节;
  • 然后将该线程加入线程组;
  • 最后调用start0方法,started变量的作用是一个标志位,如果start0方法执行失败,那么将该线程从线程组中移除。

join方法

join方法代码如下所示,它利用isAlive方法和Object类的wait方法实现,这样做的原因是当一个线程终止时,其notifyAll方法会被调用。

public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

public final synchronized void join(long millis, int nanos) throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    join(millis);
}

public final void join() throws InterruptedException {
    join(0);
}

interrupt方法

interrupt方法用于中断一个线程,其代码如下所示。上文提到blocker变量表示线程在执行可中断IO操作时阻塞该线程的对象,如果存在那么先执行interrupt0方法再去中断IO操作,否则直接执行interrupt0方法。

public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();

    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

使用时需注意中断状态的变化:

  • 若线程阻塞在Object类的wait方法、Thread类自己的join和sleep方法调用上,那么中断状态会被清除,线程会收到InterruptedException;
  • 若线程阻塞在InterruptibleChannel的IO操作上,那么通道会被关闭,中断状态会被置位,线程会收到ClosedByInterruptException;
  • 若线程阻塞在Selector上,那么中断状态会被置位,select操作会立即返回,返回值可能是一个非零值;
  • 其他情况下中断状态都会被置位。

getState方法

线程的状态可由getState方法获得,在线程内部,状态是由threadStatus这个整型值表示的,getState方法将其转换成枚举值。用整型值表示线程状态的原因是线程的很多方法都是JNI方法,没有Java枚举的概念。

public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

toThreadState方法如下:

public static State toThreadState(int var0) {
    if ((var0 & 4) != 0) {
        return State.RUNNABLE;
    } else if ((var0 & 1024) != 0) {
        return State.BLOCKED;
    } else if ((var0 & 16) != 0) {
        return State.WAITING;
    } else if ((var0 & 32) != 0) {
        return State.TIMED_WAITING;
    } else if ((var0 & 2) != 0) {
        return State.TERMINATED;
    } else {
        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
    }
}

整型值与枚举值的对应关系不能从上述方法得到,而应该在Hotspot源码路径hotspot/src/share/vm/classfile/javaClasses.hpp中寻找:

整型值 枚举值
0 NEW
5 RUNNABLE
2 TERMINATED
401 WAITING
417 TIMED_WAITING
1025 BLOCKED

JNI方法

Thread类中比较重要的JNI方法如下所示:

private static native void registerNatives();
public static native Thread currentThread();
public static native void yield();
public static native void sleep(long millis) throws InterruptedException;
private native void start0();
private native boolean isInterrupted(boolean ClearInterrupted);
public final native boolean isAlive();
public static native boolean holdsLock(Object obj);
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);

以registerNatives方法为例,该方法在Thread类的静态代码块执行,其对应的JNI方法可在OpenJDK的源码jdk/src/share/native/java/lang/Thread.c中找到定义。该方法调用RegisterNatives函数向JVM注册了其他的JNI方法如start0、isAlive和sleep等。

#include "jni.h"
#include "jvm.h"

#include "java_lang_Thread.h"

#define THD "Ljava/lang/Thread;"
#define OBJ "Ljava/lang/Object;"
#define STE "Ljava/lang/StackTraceElement;"
#define STR "Ljava/lang/String;"

#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

#undef THD
#undef OBJ
#undef STE
#undef STR

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

这些JNI方法的实现请见下一篇文章。

你可能感兴趣的:(Java线程Thread类详解)