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

# 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 保证线程编号的不重复
posted @ 2024-03-12 21:20  yangruomao  阅读(5)  评论(0编辑  收藏  举报