Java中的Thread

前言

想必各位都对线程这个词不陌生,我们都知道Java中可以通过Thread、Rannable、Callable和线程池来创建一个线程。但是一个线程的创建、运行到结束到底是一个什么样的过程呢?我们以一段代码为入口点来看看Java到底是如何弄的线程。

1
2
3
4
5
public static void main(String[] args) {
new Thread(() -> {
System.out.println("run...");
}).start();
}


源码

基本属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// 线程名
private volatile String name;
// 优先级
private int priority;
// 没有用到。可能在虚拟机调用的时候会有用。
private Thread threadQ;
private long eetop;

// 是否单步执行此线程
private boolean single_step;

// 是否是守护线程
private boolean daemon = false;

// JVM状态
private boolean stillborn = false;

// run方法实现
private Runnable target;

// 当前线程的线程组
private ThreadGroup group;

// 这个线程的上下文类加载器
private ClassLoader contextClassLoader;
// 线程继承的上下文
private AccessControlContext inheritedAccessControlContext;

// 用于编号线程名,单调递增
private static int threadInitNumber;

// 与当前线程相关的ThreadLocal值
ThreadLocal.ThreadLocalMap threadLocals = null;

// 当前线程继承的ThreadLocal值
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

// 线程的栈大小
private long stackSize;

// 在本机线程终止后持续存在的 JVM 私有状态。
private long nativeParkEventPointer;

// 线程Id
private long tid;
// 用于生成线程Id
private static long threadSeqNumber;

// 线程状态
private volatile int threadStatus = 0;

// 没有用到。可能在虚拟机调用的时候会有用。
volatile Object parkBlocker;

// 中断标志
private volatile Interruptible blocker;
// 中断时锁住的对象
private final Object blockerLock = new Object();

// 最小优先级
public final static int MIN_PRIORITY = 1;
// 默认优先级
public final static int NORM_PRIORITY = 5;
// 最大优先级
public final static int MAX_PRIORITY = 10;

/**
* 线程状态
*/
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}

开始到结束

静态代码块

1
2
3
4
5
private static native void registerNatives();
static {
// 方便调用本地方法块
registerNatives();
}

init()

主要就是初始化线程的一些基本信息(线程名、栈大小、线程组、父线程、优先级、上下文信息等)和安全检测。

注意:这里并没有将线程添加到线程组里面,只是增加线程组中未启动线程的计数。addUnstarted()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public Thread(Runnable target) {
// param1: 线程组
// param2: Runnable
// param3: 线程名(从0开始递增)
// param4: 分配的栈大小(初始为0)
init(null, target, "Thread-" + nextThreadNum(), 0);
}

private static synchronized int nextThreadNum() {
// 从0开始递增
return threadInitNumber++;
}

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

/**
* 线程的初始化方法
* 主要就是初始化线程的一些基本信息(线程名、栈大小、线程组、父线程、优先级等)
* @param g 线程组,一般为null,除非有传入。
* @param target Runnable
* @param name 线程名。从0开始,单调递增
* @param stackSize 新线程所需的堆栈大小,或为零表示将忽略此参数。
* @param acc 上下文信息。如果为null,就获取当前线程的上下文信息
* @param inheritThreadLocals 是否从构造线程继承可继承线程局部的初始值
*/
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) {

if (security != null) {
g = security.getThreadGroup();
}

if (g == null) {
// 就算线程组为null也会设置为父线程的线程组
g = parent.getThreadGroup();
}
}

// 安全检查
g.checkAccess();

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);
// 栈大小
this.stackSize = stackSize;

/* Set thread ID */
// 设置线程Id。从0开始单调递增
tid = nextThreadID();
}

start()

首先会判断该线程状态是否是NEW,是的话就将该线程添加到线程组里面(因为在init()方法里面只是增加了计数,并未添加到线程组里面),然后调用Native方法start0()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public synchronized void start() {

// 0 对应 NEW
if (threadStatus != 0)
throw new IllegalThreadStateException();

// 将线程添加到线程组中
group.add(this);

boolean started = false;
try {
// 调用native方法。这个方法会修改线程的状态为Running
start0();
// 在start0()方法执行完成之后,代表线程已经要开始运行了
// 会直接执行run()方法,而主线程可能还在执行started = true
started = true;
} finally {
try {
if (!started) {
// 开启失败
group.threadStartFailed(this);
}
} catch (Throwable ignore) {

}
}
}

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

exit()

被系统方法调用。将该线程从线程组中删除,如果线程组是守护线程组并且没有活动线程并且没有未启动的线程并且没有子线程组,就将该线程组进行销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 退出方法。是被系统调用的。
*/
private void exit() {
if (group != null) {
// 将该线程从线程组中删除
// 如果该线程组是守护线程组并且活动线程数为0
// 并且未启动的线程为0并且没有子线程组,销毁该线程组
group.threadTerminated(this);
group = null;
}
target = null;
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}

void threadTerminated(Thread t) {
synchronized (this) {
// 将指定线程从线程组中删除。就是从线程数组里面删除该线程
remove(t);

// 如果线程组中活动线程数为0,唤醒所有线程
if (nthreads == 0) {
notifyAll();
}

// 如果是守护线程组并且活动线程数为0并且未启动的线程为0并且没有子线程
if (daemon && (nthreads == 0) &&
(nUnstartedThreads == 0) && (ngroups == 0))
{
// 销毁该线程组
destroy();
}
}
}


Thread状态

Thread状态的变化都是通过一些native方法进行改变的。

getState()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public State getState() {
// 我们可以看到这里调用的是虚拟机里面的方法
return sun.misc.VM.toThreadState(threadStatus);
}

// class: sun.misc.VM;
/**
* 通过&运算符来计算线程的状态。
*/
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;
}
}
作者

zhaommmmomo

发布于

2021-08-01

更新于

2023-06-27

许可协议