java线程基础巩固---构造Thread对象你也许不知道的几件事

时间:2024-04-09 15:33:33

关于Thread的构造在JDK文档中如下:

java线程基础巩固---构造Thread对象你也许不知道的几件事

之后会把上面所有的构造都会学习到,这次主要是去研究一下图上标红的默认构造,当然大家肯定对于它都有些不屑,这有啥可学的,不new一个然后start线程不就启动了,属于线程最最基础的东东,然后它的背后并非我们都知道,所以下面开始学习。

Thread常规知识:

先来看一下默认构造的源码:

java线程基础巩固---构造Thread对象你也许不知道的几件事

先了解一下默认线程名的起名规则,看下nextThreadNum():

java线程基础巩固---构造Thread对象你也许不知道的几件事

原来是有一个静态计数的变量,那也就是说默认new出来的线程是以"Thread-0、Thead-1"这样的规则来命名的,用代码来验证下:

java线程基础巩固---构造Thread对象你也许不知道的几件事

编译运行:

java线程基础巩固---构造Thread对象你也许不知道的几件事

果真如预期,但是目前线程中并未做任何事情,而我们知道可以给线程构造中传一个Runnable接口:

java线程基础巩固---构造Thread对象你也许不知道的几件事

那在默认构造Thread的时候有没有一个空的Runnable去执行呢?看源码【贴出关键代码】:

    public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
} private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null);
}

从代码中可以看到并未自动创建一个Runnable,而是直接传的null,那最终在init()中是如何搞的呢?

private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
} this.name = name.toCharArray(); 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();
}
} /* 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 (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();
}

只看核心代码,可以看到将Thread的成员变量target赋值为null了,

java线程基础巩固---构造Thread对象你也许不知道的几件事

那此时我们执行创建的线程会调用start()方法,那接着看下它:

public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this); boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
} private native void start0();

最终转到C层去执行线程的启动了,而底层的代码暂且不深纠了,这里要知道C层会去调用Thread的run()方法,所以转到run方法去看一眼:

java线程基础巩固---构造Thread对象你也许不知道的几件事

但是!!!可以重写它的run方法来执行我们的动作,如下:

java线程基础巩固---构造Thread对象你也许不知道的几件事

编译运行:

java线程基础巩固---构造Thread对象你也许不知道的几件事

那接着修改代码:

java线程基础巩固---构造Thread对象你也许不知道的几件事

java线程基础巩固---构造Thread对象你也许不知道的几件事

java线程基础巩固---构造Thread对象你也许不知道的几件事

接着在创建runnable的时候也手动给它取个名字,如下:

java线程基础巩固---构造Thread对象你也许不知道的几件事

java线程基础巩固---构造Thread对象你也许不知道的几件事

这样关于Thread的构造就学习了四种了,当然也是最经常使用的,比较简单:

java线程基础巩固---构造Thread对象你也许不知道的几件事

ThreadGroup的概念及守护线程初探:

关于这个内容可能就不是太清楚啦,可以看一下剩下线程构造函数中:

java线程基础巩固---构造Thread对象你也许不知道的几件事

全是跟ThreadGroup相关,貌似实际工作中完全木有接触过它,所以说首先得了解它,从哪了解呢,直接从源码:

public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
} private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
} this.name = name.toCharArray(); 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();
}

} /* 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 (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();
}

从源码中可以看到,默认无参的构造函数并未指定ThreadGroup,当在执行init()方法时发现没有ThreadGroup,则会获得父线程的ThreadGroup,而父线程可以通过currentThread()方法获取,而我们在之前【http://www.cnblogs.com/webor2006/p/7682063.html】有介绍过,对于创建一个线程实际是有两个线程,其中有一个就是main线程,也就是用来启动我们自己创建的线程的,那是不是我们线程的parent就是main呢?下面来做实验论证:

java线程基础巩固---构造Thread对象你也许不知道的几件事

java线程基础巩固---构造Thread对象你也许不知道的几件事

那如果此时新建一个线程,然后再看一下它的ThreadGroup,如下:

java线程基础巩固---构造Thread对象你也许不知道的几件事

java线程基础巩固---构造Thread对象你也许不知道的几件事

那说明创建线程时未指定ThreadGroup,那会以父线程的ThreadGroup作为该线程的ThreadGroup,刚好跟源码逻辑吻和,那是不是说子线程将和父线程在同一个ThreadGroup中?也就是说目前ThreadGroup中应该包含了2个线程,那我们打印看一下:

java线程基础巩固---构造Thread对象你也许不知道的几件事

编译运行:

java线程基础巩固---构造Thread对象你也许不知道的几件事

这是在MAC上的表现,但是!!!如果在windows平台其输出并非如我们所预期的,下面在windows下运行看一下结果:

java线程基础巩固---构造Thread对象你也许不知道的几件事

java线程基础巩固---构造Thread对象你也许不知道的几件事

总结:

  • 创建线程对象Thread,默认有一个线程名,以Thread-开头,从0开始计数:Thread-0、Thread-1、Thread-2等。
  • 如果在构造Thread()的时候没有传递Runnable或者没有复写Thread的run方法,该Thread将不会调用任何东西;如果传递了Runnable接口的实例,或者复写的Thread的run方法,则会执行该方法的逻辑单元(逻辑代码)。
  • 如果构造线程对象时未传入ThreadGroup,Thread会默认获取父线程的TreadGroup作为该线程的ThreadGroup,此时子线程将和父线程在同一个ThreadGroup中。