线程名称设置的相关细节

Posted by xdshent on July 16, 2020

理论

前提

记得之前在一本书上看到说线程运行之后名字不可改,只有在调用start()方法前才可以(大概是这么个意思),但是最近在看java.lang.Thread源码的时候发现并非如此,首先看 java.lang.ThreadsetName(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)方法。