打开JDK的源码可以看到我们构造Thread的时候,默认的线程的名字是以Thread-开头,从0开始计数:即Thread-0、Thread-1 。。。。
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}n, where n is an integer.
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
想修改线程的名字要趁早,在线程启动之前,还有一次修改线程名字的机会,一旦线程启动,名字就不能修改。
下面是在Thread中修改名字的代码:
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);
}
}
Thread的所有构造函数,最终都会去调用一个静态方法init,我们截取片段代码对其进行分析,不难发现新创建的任何一个线程都会有一个父线程:
这里截取Thread的init的部分代码:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread(); //获取当前运行的线程作为父线程
SecurityManager security = System.getSecurityManager();
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
上面代码中的 currentThread() 是获取当前线程,在线程生命周期中,我们说过线程的最初状态为NEW,没有执行 start 方法之前,它只能算是一个 Thread 的实例,并不意味着一个新的线程被创建,因此 currentThread() 代表的将会是创建它的那个线程,因此我们可以得出以下结论。
我们都知道 main 函数所在的线程是由 JVM 创建的,也就是 main 线程,那就意味着我们前面创建的所有线程,其父线程都是 main 线程。
在Thread的构造函数中,可以显示的指定线程的Group,也就是ThreadGroup,下面看init方法的中间部分:
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
源码的意思: 如果在构造Thread的时候没有显示的指定一个ThreadGroup,那么子线程将会被加入父线程所在的线程组。
public static void main(String[] args) {
PrintStream out=System.out;
Thread t1=new Thread("t1");
ThreadGroup group=new ThreadGroup("group1");
Thread t2=new Thread(group,"t2");
ThreadGroup mainGroup=Thread.currentThread().getThreadGroup();
out.println("Main Thread Group:"+mainGroup.getName());
out.println(t1.getThreadGroup()==mainGroup);
out.println(t2.getThreadGroup().getName());
out.println(t2.getThreadGroup()==group);
}
输出
Main Thread Group:main
true
group1
true
得出结论:
基本性质。
线程是否为守护线程和它的父线程有很大的关系,如果父线程是正常线程,则子线程也是正常线程,反之亦然,如果你想要修改它的特性则可以借助 setDaemon 方法。isDaemon() 方法可以判断该线程是不是守护线程。
看个例子:
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while (true) {
System.out.println("t running....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
//t.setDaemon(true);
System.out.println(t.isDaemon());
t.start();
Thread.sleep(2000);
System.out.println("Main 线程结束");
}
守护线程的作用
上面注释了t.setDaemon(true);运行结果如下,发现main 线程结束了,但是里面的线程没有结束。
可以看到,即使main结束了,t线程仍在执行
但是如果不注释t.setDaemon(true);,当main线程结束,里面的线程就会结束。
如果一个 JVM 进程中没有一个非守护线程,那么 JVM 会退出,也就是说守护线程具备自动结束生命周期的特性,而非守护线程则不具备这个特点, 试想一下如果 JVM 进程的垃圾回收线程是非守护线程,如果 main 线程完成了工作,则 JVM 无法退出,因为垃圾回收线程还在正常的工作。再比如有一个简单的游戏程序,其中有一个线程正在与服务器不断地交互以获取玩家最新的金币、武器信息,若希望在退出游戏客户端的时候,这些数据同步的工作也能够立即结束,等等。
守护线程经常用作与执行一些后台任务,因此有时它也被称为后台线程,当你希望关闭某些线程的时候,或者退出 JVM 进程的时候,一些线程能够自动关闭,此时就可以考虑用守护线程(setDaemon())为你完成这样的工作。