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方法的实现请见下一篇文章。