java thread参数_Java Thread构造参数源码详细介绍

Thread类是一个构建线程的关键类,通过传递一个实现了Runnable接口的类就可以简单构造出一个线程对象,下面就来看看有关Thread类的一些基础知识点吧(本文略长请耐心阅读,相信你一定受益匪浅)。

Thread一共有8种(public修饰)构造函数和一种(default修饰)默认构造函数,分别如下所示:

public Thread() {

init(null, null, "Thread-" + nextThreadNum(), 0);

}

public Thread(Runnable target) {

init(null, target, "Thread-" + nextThreadNum(), 0);

}

Thread(Runnable target, AccessControlContext acc) {

init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);

}

public Thread(ThreadGroup group, Runnable target) {

init(group, target, "Thread-" + nextThreadNum(), 0);

}

public Thread(String name) {

init(null, null, name, 0);

}

public Thread(ThreadGroup group, String name) {

init(group, null, name, 0);

}

public Thread(Runnable target, String name) {

init(null, target, name, 0);

}

public Thread(ThreadGroup group, Runnable target, String name) {

init(group, target, name, 0);

}

public Thread(ThreadGroup group, Runnable target, String name,long stackSize) {

init(group, target, name, stackSize);

}

通过观察以上的构造函数,其实不难发现,所有的构造方法最终都是调用了一个叫"init()"的方法。接下来我们就重点来关注下这个“init()”方法到底做了一些什么事,完成了哪些操作。

通过观察源码可以发现“init()”方法也有两个重载,但是最终的调用还是参数比较多的那个方法。其实根据多阅读源码的经验,不难发现,把参数最多的一个方法封装好,可以根据方法的重载来实现不同参数的方法实现。然后我们重点来解读参数较多的“init()”方法。

private void init(ThreadGroup g, Runnable target, String name,long stackSize) {

init(g, target, name, stackSize, null, true);

}

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) {

if (security != null) {

g = security.getThreadGroup();

}

if (g == null) {

g = parent.getThreadGroup();

}

}

g.checkAccess();

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);

}

this.stackSize = stackSize;

tid = nextThreadID();

}

乍一看上面的“init()”里面的内容确实有点多,请不要着急,静下心来跟着我的思路一点一点的阅读。

if (name == null) {

throw new NullPointerException("name cannot be null");

}

第一步,就是对线程名字进行一个判断,判断是不是等于空,如果传递的线程名字是空值的话,那就会抛出空指针异常来提醒用户。也许你一时间会诧异,为什么之前构造的线程没有传递名称,他也能正常运行呢?比如下面这样:

public class MyRunnable implements Runnable {

@Override

public void run() {

System.out.println("没有构造线程名称");

}

}

public class MyRunnableTest {

public static void main(String[] args) {

Thread thread = new Thread(new MyRunnable());

thread.start();

}

}

不错,这样是可以正确运行的,也是能够输出“没有构造线程名称”这句话的。其实这个还要回到文章刚开始的地方,8个公共的构造方法的重载上。通过跟进上述例子中的源码不难发现,上述例子中走的构造方法是下面的这个:

public Thread(Runnable target) {

init(null, target, "Thread-" + nextThreadNum(), 0);

}

也许细心的你已经发现了,构造方法中会默认帮我们生成一个以"Thread-"开头整数结尾的线程名。因为“nextThreadNum()”使用了“synchronized”关键字修饰,所以他是线程安全的,不必担心多个线程会引起数字错乱的问题。

private static int threadInitNumber;

private static synchronized int nextThreadNum() {

return threadInitNumber++;

}

第二步,就是给线程赋予名字,这个名字如果你指定了一个名字,那线程名就是你指定的。如果没有指定,那就是构造函数给你默认生成的一个。

我们都应该知道一个线程的构造和启动,肯定是由另一个线程来完成的。因此下面这个代码就是获取构造这个线程的父线程:

Thread parent = currentThread();

而“currentThread()”方法是一个本地方法,也就是交给底层去操作了。我们无须关系它到底实现了什么,只需要先知道他为我们拿到了构造这个线程的父线程就行了。

public static native Thread currentThread();

第三步,判断我们传递的“ThreadGroup”是不是为空,目的就是显示的为线程对象设置一个线程组,如果没有显示的设置线程组(也就是“g == null”条件成立)则会去SecurityManager获得线程组(SecurityManager具体含义后面文章再讲)。如果还是没有获取到则设置为父线程所归属的线程组。

SecurityManager security = System.getSecurityManager();

if (g == null) {

if (security != null) {

g = security.getThreadGroup();

}

if (g == null) {

g = parent.getThreadGroup();

}

}

第四步,就是做一些安全检查,这里不太重要,也不必要过于纠结。

g.checkAccess();

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;

}

第八步,是设置该线程的继承的AccessControlContext(AccessControlContext用于根据其封装的上下文做出系统资源访问决策,具体的介绍可以查看API的介绍)。

this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();

第九步,是设置与此线程有关的InheritableThreadLocal值,它由一个ThreadLocalMap保存(后期文章讲ThreadLocalMap会着重讲到它,这里可以简单理解为一个map)。

if (inheritThreadLocals && parent.inheritableThreadLocals != null){

this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

}

第十步,是设置新线程的所需堆栈大小,如果你了解JVM Stack(虚拟机栈)那么这个栈的深度应该就很容易理解。如果不了解的话,就认为它开辟了一块内存空间即可。

this.stackSize = stackSize;

第十一步,是设置线程的ID,每一个线程都有一个唯一的ID进行标识。可以看到线程ID的设置是调用了“nextThreadID()”而“nextThreadID()”也用了“synchronized ”关键字进行修饰,所以也是线程安全的

tid = nextThreadID()

private static long threadSeqNumber;

private static synchronized long nextThreadID() {

return ++threadSeqNumber;

}

给一个图希望加深印象(画图写作真的很累,希望能帮到你,顺便公众号关注我下哈*_*):

a211096f732386ae898b002479bdf5e7.png

到此为止,一个基本的线程对象就构建完成了,不过需要注意的是他还没有启动,只是简单的构造了一个线程对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值