java线程基础巩固

时间:2022-03-13 12:03:07

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

java线程基础巩固

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

Thread常规知识:

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

java线程基础巩固

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

java线程基础巩固

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

java线程基础巩固

编译运行:

java线程基础巩固

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

java线程基础巩固

那在默认构造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线程基础巩固

那此时我们执行创建的线程会调用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线程基础巩固

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

java线程基础巩固

编译运行:

java线程基础巩固

那接着修改代码:

java线程基础巩固

java线程基础巩固

java线程基础巩固

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

java线程基础巩固

java线程基础巩固

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

java线程基础巩固

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

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

java线程基础巩固

全是跟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线程基础巩固

java线程基础巩固

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

java线程基础巩固

java线程基础巩固

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

java线程基础巩固

编译运行:

java线程基础巩固

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

java线程基础巩固

java线程基础巩固

总结:

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