2.init()
package ThreadPackage;
public class ThreadTest {
public static void main(String[] args) {
// 创建一个线程,显式调用Thread的构造函数
Thread thread = new Thread(() -> {
System.out.println("线程运行");
}, "MyThread"); // 传入Runnable和线程名称
// 启动线程
thread.start();
}
}
552 row:
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, name)}.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this thread's run method is invoked.
*
* @param name
* the name of the new thread
*/
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
-
这个构造方法的作用是分配一个新的
Thread
对象。 -
这个构造方法的行为等同于调用另一个构造方法
Thread(ThreadGroup, Runnable, String)
,并将ThreadGroup
参数设置为null
。ThreadGroup
是 Java 提供的一个类,用于将多个线程组织在一起。线程组可以看作是一个容器,里面包含了多个线程以及其他线程组(线程组可以嵌套)。通过线程组,你可以对一组线程进行统一的管理。
-
@param target
:解释了target
参数的作用。如果target
不为null
,线程启动时会调用target
的run()
方法;如果为null
,则调用线程自身的run()
方法。 -
@param name
:解释了name
参数的作用,表示线程的名称。
/**
* Initializes a Thread with the current AccessControlContext.
* @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
// 新线程所需的堆栈大小,或 0 表示忽略此参数
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
// 要继承的 AccessControlContext,如果为空,则为 AccessController.getContext()
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
// 如果 {@code 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();
}
理解:
这段代码展示了 Thread
类的 init()
方法的实现细节,它是线程初始化的核心逻辑。为了更好地理解这些代码在做什么,我们可以将其分解为几个关键部分,并用形象的方式解释每个部分的作用。
1. init()
** 方法的作用**
init()
方法是 Thread
类的一个私有方法,用于初始化线程对象的内部状态。它会设置线程的名称、线程组、优先级、是否为守护线程(daemon)、线程的上下文类加载器、继承的线程本地变量等。
这个方法是 Thread
类的构造方法的核心部分,所有的构造方法最终都会调用这个方法来完成线程的初始化。
2. 形象化理解:线程的初始化过程
可以将线程的初始化过程比喻为创建一个“工人”(线程)并将其分配到一个“部门”(线程组)中,同时为这个工人设置一些属性,比如名字、工作任务、权限、工具等。
3. 代码分解与解释
3.1 检查线程名称
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
- 作用:确保线程的名称不为空。如果名称为
null
,会抛出NullPointerException
。 - 形象化:每个工人(线程)都需要一个名字,名字不能为空。
3.2 获取当前线程和安全管理器
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
- 作用:
currentThread()
获取当前正在运行的线程(即创建这个新线程的线程)。System.getSecurityManager()
获取当前的安全管理器(SecurityManager
),用于检查线程的权限。
- 形象化:当前线程是“主管”,它负责创建新的工人(线程)。安全管理器是“安全检查员”,负责检查新工人是否符合安全要求。
3.3 确定线程组
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
- 作用:
- 如果没有显式指定线程组(
g == null
),则:- 如果有安全管理器,询问安全管理器应该使用哪个线程组。
- 如果安全管理器没有指定线程组,则使用当前线程的线程组。
- 如果没有显式指定线程组(
- 形象化:如果没有明确指定新工人(线程)属于哪个部门(线程组),就让安全检查员(安全管理器)决定;如果安全检查员也没有意见,就让主管(当前线程)决定。
3.4 检查线程组的访问权限
g.checkAccess();
- 作用:检查当前线程是否有权限访问指定的线程组。
- 形象化:确保新工人(线程)被分配到的部门(线程组)是合法的,主管(当前线程)有权限将工人分配到这个部门。
3.5 添加未启动的线程到线程组
g.addUnstarted();
- 作用:将新线程标记为“未启动”状态,并将其添加到线程组中。
- 形象化:将新工人(线程)登记到部门(线程组)的员工名单中,但还没有开始工作。
3.6 设置线程的属性
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
- 作用:
- 设置线程所属的线程组。
- 设置线程是否为守护线程(
daemon
),默认继承父线程的守护状态。 - 设置线程的优先级,默认继承父线程的优先级。
- 形象化:新工人(线程)继承了主管(当前线程)的一些属性,比如是否是“夜班工人”(守护线程)以及工作优先级。
3.7 设置上下文类加载器
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
- 作用:设置线程的上下文类加载器(
contextClassLoader
),用于加载线程运行时需要的类。 - 形象化:为新工人(线程)分配一个工具箱(类加载器),工具箱的内容由主管(当前线程)决定。
3.8 设置继承的线程本地变量
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
- 作用:如果
inheritThreadLocals
为true
,则从父线程继承线程本地变量(ThreadLocal
)。 - 形象化:新工人(线程)可以继承主管(当前线程)的一些私人工具(线程本地变量)。
3.9 设置线程的堆栈大小
this.stackSize = stackSize;
- 作用:设置线程的堆栈大小。如果为
0
,表示使用默认堆栈大小。 - 形象化:为新工人(线程)分配工作空间的大小。
3.10 分配线程 ID
tid = nextThreadID();
- 作用:为线程分配一个唯一的 ID。
- 形象化:为新工人(线程)发放一个工号。
4. 总结:init()
** 方法的整体流程**
- 检查线程名称是否为空。
- 获取当前线程和安全管理器。
- 确定线程所属的线程组。
- 检查线程组的访问权限。
- 将线程添加到线程组中。
- 设置线程的属性(名称、守护状态、优先级、类加载器等)。
- 继承父线程的线程本地变量(如果需要)。
- 设置线程的堆栈大小。
- 分配线程的唯一 ID。
5. 形象化总结
可以将 init()
方法比喻为“创建一个新工人并分配到部门”的过程:
- 给工人起一个名字。
- 确定工人属于哪个部门(线程组)。
- 检查工人是否符合安全要求。
- 将工人登记到部门的员工名单中。
- 为工人设置属性(是否夜班、优先级、工具箱等)。
- 如果需要,继承主管的一些私人工具。
- 分配工作空间和工号。
通过这个过程,线程对象就被完整地初始化了,准备好可以被启动(start()
)并执行任务了。
Runnable
= 操作系统中的 Ready(就绪态) 和 Running
(运行态)