深入理解Thread构造函数
Thread构造函数汇总以及解析
查看JDK官方文档我们可以发现Thread类有以下的构造函数
1、Thread的命名
(1)无name参数的构造方法
-
Thread()
-
Thread(Runnable target)
-
Thread(ThreadGroup group, Runnable target)
我们查看Thread()的方法源码不难发现,如果我们在创建线程时并不指定名字的话,线程将会以Thread+一个自增数字作为该线程的名字,这个自增数字在jvm创建的时候由于其为成员变量且为基础类型初始化为0
/**
* 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-"+}<i>n</i>, where <i>n</i> 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++;
}
(2)有name参数的构造方法
-
Thread(Runnable target, String name)
-
Thread(String name)
-
Thread(ThreadGroup group, Runnable target, String name)
-
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
-
Thread(ThreadGroup group, String name)
通过查看以上构造函数可以发现我们在构建Thread的时候,我们可以显式的设置线程的名字,这样可以利于我们进行程序问题的排查以及定位,同时Thread还提供了一个对其名字进行修改的方法setName(),具体源码
/**
* Changes the name of this thread to be equal to the argument
* <code>name</code>.
* <p>
* First the <code>checkAccess</code> method of this thread is called
* with no arguments. This may result in throwing a
* <code>SecurityException</code>.
*
* @param name the new name for this thread.
* @exception SecurityException if the current thread cannot modify this
* thread.
* @see #getName
* @see #checkAccess()
*/
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为0的线程修改名字才可生效,为了验证我们做以下测试
public class ModifyThreadName {
public static void main(String[] args) {
Thread test1=new Thread("test1"){
@Override
public void run(){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
test1.start();
test1.setName("test2");
System.out.println("test1线程名字:"+test1.getName()+" 线程状态:"+test1.getState());
Thread test3=new Thread("test3"){
@Override
public void run(){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};;
test3.setName("test4");
test3.start();
System.out.println("test3线程名字:"+test3.getName()+" 线程状态:"+test3.getState());
}
}
输出为
从输出可以看到线程无论是在NEW状态或者RUNNABLE状态下都可以使用setName修改名字,并且修改成功,但是我们启动test3线程后运行,再使用jvisualvm查看下
从上图中我们可以看到test2线程的名字还是为test2,而test3的名字已经改为test4,所以我们可以确定setName方法可以对新建状态的线程修改名字并且当线程运行时也是修改后的名字,但是如果线程处于运行中的状态的时候使用setName方法只能修改java对象层面的名字,线程实际的名字并不会随之发生改变。
2、Thread的父子关系
我们在每个Thread的构造函数中都会发现有一个init方法,我们看下源码
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
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();
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都有一个parent,parent正是创建该线程的当前线程,所以可以说一个线程的创建肯定是由另一个线程完成的,main函数的线程是由jvm创建的,可以这样说,所有的线程的父线程都是main线程;另外,源码中也可以看出如果没有显式的指定线程组,那新建的进程将会加入到父线程所在的线程组当中
3、Thread中的stackSize参数
我们都知道,在启动java程序的时候可以指定 Xss的大小,该值指定了创建线程后其所能使用的虚拟机栈的大小,这里说的stackSIze是指定单个线程虚拟机栈的大小,而xss则是全局的设置
4、Runnable参数
该参数与Thread的关系我在 Thread 所使用到的设计模式(一)所涉及过,这里就不多说,实际上就是 Thread类负责对线程本身的控制,而Runnable则是更趋向于逻辑执行单元的部分