理论
前提
记得之前在一本书上看到说线程运行之后名字不可改,只有在调用start()方法前才可以
(大概是这么个意思),但是最近在看java.lang.Thread
源码的时候发现并非如此,首先看 java.lang.Thread
的setName(String name)
方法:
1
2
3
4
5
6
7
8
9
10
11
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
可以看到在设置线程名称的时候进行了threadStatus
的判断,然后调用了native
方法,那么这个threadStatus
变量表示的是什么? 往上追溯可以看到:
1
2
3
4
5
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
也就是说
threadStatus
表示的是线程的NEW
状态(还未调用start方法),线程的状态转换详见线程的生命周期
猜测
threadStatus = 0
既然表示NEW
状态那么在线程启动之前还没有在OS层产生对应的线程就不能调用native
方法来设置名称,只有启动之后OS层有对应的资源才可以setNativeName
,线程名字是可以在运行时修改的,只不过对运行时线程名字的修改允不允许就属于编码规范层面的事了。
验证
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) {
Thread t = new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" start ");// 1
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+" done");// 2
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.setName("线程新名称"); // 3
System.out.println("main修改线程名称完毕"); // 4
}
输出
主线程启动
t
线程之后等待1秒钟(确保t线程处于运行中),t
线程运行之后先打印线程名 + start
然后休眠5秒,此时主线程苏醒进行进行修改t
线程名称,然后打印结束语,等t
线程苏醒打印线程名 + done
发现线程名前后不同,如下:
Thread-0 start
main修改线程名称完毕
线程新名称 done
OpenJDK
追到OpenJDK的src/share/native/java/lang/Thread.c
方法得知setNativeName(name)
对应的是 (void *)&JVM_SetNativeThreadName
方法,然后到src/share/vm/prims/jvm.cpp
可以发现第3493
行有一个os::set_native_thread_name(thread_name)
方法。