>我一直在我的 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()
时,都将获得对当前正在执行的线程对象的引用(对调用此方法的线程的引用)。