是的,因为GC只能收集任何线程都不可达的对象,而Thread必须持有对其可运行对象的引用(否则它将无法调用它)。因此,在您的线程正在运行时,您的可运行对象显然是可达的。
无论执行所需的语义如何,只要该对象被这个新线程或任何其他线程仍然可访问,它就不会被GC处理;至少在足以调用您的Runnable的run()方法的时间内,并且整个线程的生命周期如果该线程能够访问Runnable实例,则该构造由JVM规范保证安全。
编辑:由于Darron一直在深入探讨这个问题,并且有些人似乎被他的论点所说服,我将根据他的想法扩展解释。假设暂时不允许除Thread本身之外的任何人调用Thread.run()。在这种情况下,Thread.run()的默认实现可能如下所示:
void run() {
Runnable tmp = this.myRunnable;
this.myRunnable = null;
if(tmp != null) {
tmp.run();
}
}
我认为在这种情况下,tmp仍然是对由Thread.run()中执行的线程可达的runnable的引用,因此不适合进行垃圾回收。
如果代码看起来像下面这样会怎样(出于某些难以理解的原因):
void run() {
Runnable tmp = this.myRunnable;
this.myRunnable = null;
if(tmp != null) {
tmp.run();
System.out.println("Executed runnable: "+tmp.hashCode());
}
}
很明显,在tmp.run()执行期间,被tmp引用的实例是不能进行垃圾回收的。
我认为达伦误以为可达只指可以通过从所有Thread实例开始追踪实例引用来找到的引用,而不是由任何执行线程都可以看到的引用。或者我错误地认为相反。
此外,达伦可以假设JIT编译器可以进行任何他想要的更改-编译器不允许更改执行代码的引用语义。如果我编写具有可达引用的代码,编译器不能优化掉该引用并在该引用处于作用域内时导致我的对象被收集。
我不知道如何实际寻找可达对象的详细信息;我只是推断出我认为必须保持的逻辑。如果我的推理不正确,则在方法中实例化并仅分配给该方法中的本地变量的任何对象都将立即符合进行垃圾回收的条件-显然这是不可能的。
此外,整个辩论都是无关紧要的。如果唯一的可达引用在Thread.run()方法中,因为runnable的run()方法没有引用它的实例,并且除了传递给run()方法的隐式this(在字节码中,而不是作为声明的参数)之外不存在对实例的其他引用,则是否收集对象实例并不重要 - 按照定义,这样做不会造成任何伤害,因为如果this被优化掉,则不需要它来执行代码。既然如此,即使达伦是正确的,在最终实践结果上,OP构想的结构也是完全安全的。无论哪种情况,都没有关系。让我再说一遍,以确保清楚 - 最终分析中这没关系。