thread类的target属性是runnable类型成员变量,通过构造方法传入并赋值,在run()中判空后执行target.run();若为null则静默跳过,不报错也不处理。

Java中Thread类的target属性空检查机制,核心就落在run()方法那一行判断上:如果target != null,才调用target.run();否则什么也不做。
target是什么?它从哪来?
private Runnable target; 是Thread类内部的一个成员变量,类型为Runnable。它代表线程真正要执行的任务逻辑。
这个target值在Thread构造过程中被赋值,比如调用new Thread(Runnable target)时,会一路走到init()方法:
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }private void init(...){ this.target = target; ... }
也就是说,你传进来的Runnable对象,最终被存进了this.target里。
run()方法里的空检查逻辑
Thread类的run()方法源码非常简洁:
public void run() {
if (target != null) {
target.run();
}
}
这里只做一件事:判空。不抛异常,不打印日志,不兜底处理——target为空,就静默跳过。
这意味着:
- 如果你用
new Thread()无参构造创建线程,target默认为null,start()后什么也不会执行 - 如果你重写了
Thread.run(),但没调用super.run(),那这层if (target != null)逻辑就被绕过了,target即使非空也不会被执行
为什么设计成“空则忽略”,而不是报错或强制要求?
这种设计体现了Java线程模型的灵活性和兼容性考虑:
- 允许用户只继承
Thread并重写run(),完全不用Runnable,此时target天然为null,不干扰自定义逻辑 - 支持“空线程”场景,比如占位、延迟初始化、或配合子类扩展做条件执行
- 避免运行时强制约束,把责任交给开发者:传了
Runnable就要确保它非空,这是调用方的契约
实际开发中要注意的坑
target空检查虽简单,但容易引发隐性问题:
-
误调
run()而非start():直接调thread.run()不会触发JVM线程调度,也不会走target分发逻辑(它只是普通方法调用),容易误以为线程跑起来了 -
传入
null给构造函数:new Thread((Runnable)null)合法,但start()后无声失败,建议用IDE警告或静态检查拦截 -
混淆继承与组合方式:若同时继承
Thread又传Runnable,且重写run()时忘了调super.run(),target.run()就永远不会执行
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











