Thread.currentThread() 方法是否总是在 Runnable 的生命周期内返回相同的对象?



>我一直在我的 Runnable#run() 方法中使用 Thread#currentThread() 来获取对当前正在执行的线程的引用。由于 Runnable 可以在执行器中并行运行多个类似的副本一段时间,因此它会始终返回相同的 Thread 对象,还是会在 Runnable 的生命周期内发生变化?

所以基本上,当我运行这种 Runnable 一段时间时,我会遇到问题吗?

class MyRunnable implements Runnable {

@Override
public void run() {
Thread current = Thread.currentThread();

while(!current.isInterrupted()) {
try {
// some processing that can take a while...
} catch(InterruptedException e) {
// some cleanup
current.interrupt();
}
}
// goodbye
}
}

你问错了问题。只要代码在同一线程中执行,表达式Thread.currentThread()的计算结果就会计算为表示该线程的同一对象。

但这与"Runnable的一生"无关。Runnable,或者更准确地说是实现可运行的类的实例,是一个可以独立于线程使用的普通对象。最值得注意的是,多个线程可能会执行同一对象的run()方法:

class MyRunnable implements Runnable {
@Override
public void run() {
Thread current = Thread.currentThread();
System.out.println(current);
}
}
MyRunnable r = new MyRunnable();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-2,5,main]

但是,这并不意味着假设为特定执行获取相同的Thread实例是错误的。

要基于示例代码构建,请执行以下操作:

class MyRunnable implements Runnable {
@Override
public void run() {
Thread current = Thread.currentThread();
while(!current.isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
current.interrupt();
}
}
System.out.println(current+" has been interrupted, exiting");
}
}
MyRunnable r = new MyRunnable();
Thread[] threads = { new Thread(r), new Thread(r), new Thread(r) };
for(Thread t: threads) t.start();
for(Thread t: threads) try {
Thread.sleep(400);
System.out.println("going to interrupt "+t);
t.interrupt();
t.join();
}
catch (InterruptedException ex) {
throw new AssertionError(ex);
}

将正确打印:

going to interrupt Thread[Thread-0,5,main]
Thread[Thread-0,5,main] has been interrupted, exiting
going to interrupt Thread[Thread-1,5,main]
Thread[Thread-1,5,main] has been interrupted, exiting
going to interrupt Thread[Thread-2,5,main]
Thread[Thread-2,5,main] has been interrupted, exiting

因此,Thread.currentThread()将在整个线程生存期内(从该线程调用时)返回相同的对象,而不是可运行的生存期,但这正是您想要的。您也可以使用

class MyRunnable implements Runnable {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread()+" has been interrupted, exiting");
}
}

达到同样的效果。但请记住,第一个示例有效,因为线程已存储在局部变量中。如果要将线程存储到对象的字段中,则必须确保该对象不会被多个线程同时使用(因为在多个线程使用共享可变状态时始终必须小心)。

每个Thread都有自己的 ID,你可以调用方法 getId() 来获取它。然后你可以确定它是否是同一个Thread.

从方法的javadocgetId()

返回此线程的标识符。线程 ID 是创建此线程时生成的正长数字。线程 ID 是唯一的,在其生存期内保持不变。当线程终止时,可以重用此线程 ID。

Thread.currentThread() 方法是否总是在 Runnable 的生命周期内返回相同的对象?

是的,只要您在特定线程中调用它,Thread.currentThread()都会返回相同的引用;但不是在Runnable的整个生命周期内,而是在当前线程的整个生命周期中返回相同的引用。

Runnable类型独立于Thread存在,你可以在没有后者的情况下使用它。例如,如果您将Runnable实例引用存储在某个变量中,并且您将相同的实例传递给Thread,则退出运行器线程不会导致该Runnable实例的释放。

您应该注意,一旦线程.start()启动(之后,JVM 在内部调用.run()),在线程完成执行后重新启动线程是非法的。通常,多次启动同一线程从来都不合法。

因此,每次调用Thread.currentThread()时,都将获得对当前正在执行的线程对象的引用(对调用此方法的线程的引用)。

最新更新