并发编程(六)java Thread.start()源码解读

244 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

1线程开启

我们先来看一下源码

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 */
        }
    }
}

2源码解读

首先我们来看方法体public synchronized void start(),其中用了synchronize关键字修饰,代表此方法有锁,并且锁对象是当前调用的对象,并且避免多线程同时启动一个线程。

if (threadStatus != 0)
    throw new IllegalThreadStateException();

如果这个线程不是初始状态就直接抛出非法线程状态异常,什么情况下会出现这种情况呢,比如一个需要执行一段时间的线程在很短的时间内连续start()方法调用了两次就会抛出异常。

并且我们可以点击看到threadStatus这个变量的定义:private volatile int threadStatus = 0;是volatile关键字修饰的,说明这个变量具有可见性和禁止指令重排序,能及时发现这个变量被其他线程改变,所以这里使用volatile关键字是非常有必要的。

group.add(this);

上一章中我们遇到的是g.addUnstarted()线程组调用未开始的线程使未开始线程数量+1。这次是添加进线程组里,并使线程组里的线程+1,未开始线程数量-1。

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 */
    }
}

这里started是一个标志状态,设置初始状态为false,然后进行本地方法start0()的调用,如果成功开启,则设置started状态为true。 完全执行完之前,线程处于 Ready,完成后,只要cpu分配执行权,我们的线程就进入了Running状态。

如果starte0()方法出现异常,则在线程组中调用threadStartFailed(this)方法,这个方法里有一个remove,就是把我们线程组中记录的这个线程删除,并线程数-1,未开始线程数量+1。

并且threadStartFailed里有synchronize代码块,防止并发修改问题,所以修改数量不会出现问题。

为什么catch里什么都没有写呢?

Start0 这个异常,会直接反馈给我们的调用线程。 如果这个线程是在main线程,那么就会走Main函数里边的 thread.start方法抛出异常,然后main函数抛出异常。如果catch中进行处理, 我们的 thread.start方法感知不到异常,导致程序的错误的继续执行。

3最后总结

线程对象在初始化完成之后,调用start()方法就可以启动这个线程。线程start()方法的含义是:当前线程(即parent线程)同步告知Java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。