首页 > 编程语言 >JUC源码讲解:逐步解析 Thread.init() 源码

JUC源码讲解:逐步解析 Thread.init() 源码

时间:2024-03-12 21:38:00浏览次数:17  
标签:JUC Thread parent ThreadGroup init 源码 线程 null name

# JUC源码讲解:逐步解析 Thread.init() 源码

抛出问题

我们在 new Thread() 时,init() 方法便会自动调用,用来创建这个线程。那么,创建线程时都发生了什么事?子线程与父线程有何关系?线程是怎么创建的?juc怎么选择 ThreadGroup? 让我们从源码中寻找答案吧!

查看源码

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

可以看到,init() 传递了 ThreadGroup(划重点),以及 name(线程名),然后调用了重名方法 init()。

为了方便大家学习,我先把这个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();
        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();
    }

我们先看第一段代码:

if (name == null) {
    throw new NullPointerException("name cannot be null");
}

this.name = name;

每一个线程都必须要有一个名字,创建时会自动起名,这里进行了反复的检查。

建议大家在写项目的时候都为线程自定义线程名,以好使用 stack log 查日志

再看下一段:

Thread parent = currentThread();

这里是找到父线程,简单地说,如果我们是在 main 方法中调用的 new Thread(), 那么 main 线程就是这个 parent

接着看下一段代码,这段代码是保证了 ThreadGroup 的可用

// 先找到系统的 ThreadGroup
SecurityManager security = System.getSecurityManager();

// 尊重用户传递的 ThreadGroup
if (g == null) {

    if (security != null) {
        // 用户没有传递,就有限使用系统的 ThreadGroup
        g = security.getThreadGroup();
    }

    if (g == null) {
        // 连系统的都找不到,就使用父线程的 ThreadGroup
        g = parent.getThreadGroup();
    }
}

这里,是代码对 ThreadGroup 的反复确认,如果 ThreadGroup 为空,并且我们找到了系统级别的 ThreadGroup,那就使用系统级别的 ThreadGroup,如果系统级的 ThreadGroup 也没有找到,就使用父线程的ThreadGroup。

这里可以看到优先级了,juc 尊重用户的 ThreadGroup,次选System的,最后才选用 parent 的

看下一段,检查线程可用,不必多说

g.checkAccess();

看下一段,这里的线程还不是开启的状态,NEW 状态的线程!,还没有 start,添加到 ThreadGroup中

g.addUnstarted(); // NEW

接着,我们看这一段核心的代码:

this.daemon = parent.isDaemon(); // 继承父线程的守护线程
this.priority = parent.getPriority(); // 继承父线程的优先级

// 击沉父线程的 contextClassLoader
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);

// 尽可能的继承父线程的 inheritableThreadLocals
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    this.inheritableThreadLocals =
    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        

子线程与父线程的关系在这段代码中昭然若揭,子线程会继承父线程的 daemon 、 priority 、contextClassLoader,会尽可能的继承父线程的 inheritableThreadLocals

最后一段代码,设置线程编号,收尾!

tid = nextThreadID();

我们点进去这个函数看一看:

private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
}

看到了吗,是sync的,juc 是这样保证线程编号的一致性的

总结

通过观察 Thread.init() 的源码,我们可以主要了解到:

  • ThreadGroup:JUC会尊重用户指定的ThreadGroup,次选 System 的,最后会选择父线程的
  • 子线程与父线程的继承关系:子线程会完全继承父线程的 daemon 、 priority 、contextClassLoader,会尽可能的继承父线程的 inheritableThreadLocals
  • Thread.init() 时的线程状态是 NEW,会被添加到 ThreadGroup 中
  • init() 时通过 sync 保证线程编号的不重复

标签:JUC,Thread,parent,ThreadGroup,init,源码,线程,null,name
From: https://www.cnblogs.com/acdongla/p/18069326

相关文章

  • Java项目源码基于springboot的家政服务平台的设计与实现
    大家好我是程序员阿存,在java圈的辛苦码农。辛辛苦苦板砖,今天要和大家聊的是一款Java项目源码基于springboot的家政服务平台的设计与实现,项目源码以及部署相关请联系存哥,文末附上联系信息。项目源码:Java基于springboot的家政服务平台的设计与实现.rar资源-CSDN文库项目简介:......
  • JUC源码讲解:线程状态转换
    JUC源码讲解:线程状态转换抛出问题一个线程,有七种(也可以说是六种)状态,究竟是哪七种呢?在什么条件下,线程会进入一个状态,又在什么条件下,线程转而进入另一种状态呢?下面我们从源码角度看一下线程的状态转换观察源码我们进去Thread.State.class中,可以看到,线程状态由一个enum定义......
  • Python基于微博的舆论分析,舆论情感分析可视化系统(V5.0),附源码
    博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w+、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌......
  • pdf.js源码解析-PDF文件的结构
    为了了解pdf.js源码的详细结构和功能,先来看看PDF的文件结构,然后才能知道pdf.js中的代码是如何解析并且为何这样操作PDF文件的。PDF文件基本是由header、body、trailer组成。header包含了这个PDF的信息,比如PDF的版本,创建时间,以及作者等。body包含了PDF文件的实际内容,比如文本,图片,......
  • Hive-源码分析一条hql的执行过程
    一、源码下载 下面是hive官方源码下载地址,我下载的是hive-3.1.3,那就一起来看下吧https://dlcdn.apache.org/hive/hive-3.1.3/apache-hive-3.1.3-src.tar.gz二、上下文<Hive-源码带你看hive命令背后都做了什么>博客中已经讲到了hive命令执行后会一直循环处理控制台输入的hq......
  • 【短剧小程序开发】费用从20元送源码到20万定制到底有什么区别?
    网络微短剧无疑是2024年最火热的蓝海项目之一。2023年国内微短剧总量已达373.9亿元,可知道全年电影总票房才549亿元。短剧的市场总量超过电影票房指日可待,预计2027年短剧总量可达1000亿元。稍微关注短剧赛道的朋友就会发现,23年至今不断会有某某短剧上线当日充值超2000万等等新......
  • [Rust] Thread 3: move keyword to resolve borrowing problem for closure
    Weofteruse movewithclosurespassedto thread::spawnbecasetheclosurewillthentakeownershipofthevaluesitusesfromtheenvironment,thustransferringowershopofthosevaluesfromonethreadtoanother. Thefollowingcodewon'twork:use......
  • 如何在Visual Studio中调试.NET源码
    今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。我一般的做法是先判断是否为null,再判断Count。看了一下Count的源码如下:1[__DynamicallyInvokable]2publicintCount3{4[__DynamicallyInvokable]5get6......
  • RocketMQ为什么这么快?我从源码中扒出了10大原因!
    大家好,我是三友~~RocketMQ作为阿里开源的消息中间件,深受广大开发者的喜爱而这其中一个很重要原因就是,它处理消息和拉取消息的速度非常快那么,问题来了,RocketMQ为什么这么快呢?接下来,我将从以下10个方面来探讨一下RocketMQ这么快的背后原因如果你对RocketMQ还不了解,可以从公众......
  • RocketMQ为什么这么快?我从源码中扒出了10个原因!
    大家好,我是三友~~RocketMQ作为阿里开源的消息中间件,深受广大开发者的喜爱而这其中一个很重要原因就是,它处理消息和拉取消息的速度非常快那么,问题来了,RocketMQ为什么这么快呢?接下来,我将从以下10个方面来探讨一下RocketMQ这么快的背后原因如果你对RocketMQ还不了解,可以从公众......