开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情
上一篇文章重拾 Java 高并发:万字长文带你探索 Thread 类源码(一),我们一起学习了 Thread 类的类图结构、成员变量、状态定义等,今天,我们将继续深入学习 Thread 类的源码,一起来看看它的构造方法和常见方法~
Thread 类的构造方法
通过类图,Thread 类中的所有构造方法如图所示:
其底层其实都调用了 init 方法,只不过是方法签名不同罢了。接下来我们一起看看 init 方法都干了些啥
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();
}
- 线程的名称为空,抛出空指针异常
- 获取父线程
- 获取系统安全管理器
- 若线程组为空,且获取系统安全管理器不为空,则从系统安全管理器中获取一个线程分组;若线程分组为空,则从父线程中获取线程分组
- 检查线程组的访问权限
- 当前线程继承父线程的相关属性
- 设置线程id
Thread 类中的构造方法是被创建 Thread 对象的线程调用的,此时,调用 Thread 的构造方法创建线程的线程就是父线程,在init()方法中,新创建的 Thread 线程会继承父线程的部分属性。
run() 方法
既然 Thread 类实现了 Runnable 接口,则 Thread 类就需要实现 Runnable 接口的run()方法,如下所示:
- 可以看到,Thread 类中的 run() 方法实现非常简单,只是调用了 Runnable 对象的 run() 方法。所以,真正的任务是运行在run()方法中的。
- 直接调用Runnable接口的run()方法不会创建新线程来执行任务,如果需要创建新线程执行任务,则需要调用Thread类的start()方法。
start() 方法
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) {
}
}
}
private native void start0();
start() 方法有以下几个关键点:
- start()方法使用 synchronized 关键字修饰,说明start()方法是同步的
- 它会在启动线程前,检查线程的状态,如果不是初始化状态,则直接抛出异常。所以,一个线程只能启动一次,多次启动是会抛出异常的。
- 调用start()方法后,新创建的线程就会处于就绪状态(如果没有分配到CPU执行),当有空闲的CPU时,这个线程就会被分配CPU来执行,此时线程的状态为运行状态,JVM会调用线程的run()方法执行任务。
sleep() 方法
- sleep() 方法会让当前线程休眠一定的时间,这个时间通常是毫秒值
- 调用sleep()方法使线程休眠后,线程不会释放相应的锁
join()方法
join()方法会一直等待线程超时或者终止,代码如下所示。
join()方法的使用场景往往是启动线程执行任务的线程,调用执行线程的join()方法,等待执行线程执行任务,直到超时或者执行线程终止。