【Java】线程 - 为什么不能多次 start?

196 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 7 天,点击查看活动详情

00、前言

“为什么 Thread 不能多次 start?”

“因为多次 start 多报错呀!”

“那会什么会报错?”

“因为不能多次 start 。”

.......

今天我们就来聊聊为什么一个线程为什么不能多次 start 呢?

如果尝试过多次 start 同一个线程的同学,都会得到一个异常——java.lang.IllegalThreadStateException

01、分析

源码分析

首先我们直接来看看源码中是如何抛出这个异常的。

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".
     */
    // Android-changed: Replace unused threadStatus field with started field.
    // The threadStatus field is unused on Android.
    if (started)
        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);

    // Android-changed: Use field instead of local variable.
    // It is necessary to remember the state of this across calls to this method so that it
    // can throw an IllegalThreadStateException if this method is called on an already
    // started thread.
    started = false;
    try {
        // Android-changed: Use Android specific nativeCreate() method to create/start thread.
        // start0();
        nativeCreate(this, stackSize, daemon);
        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 去标识实现,当第一次调用完 start() 后,started 标识就会变为 true,当再次调用 start() 方法后,就会抛出异常。而对话中提出疑问的同学,应该是想知道源码中为什么要这么设计?那这个就需要提到线程的状态了。

线程状态

线程的状态有五种,分别为创建就绪运行中阻塞结束。它们之间的状态流转可能性,如下图所示。(注意:箭头的方向)

image.png

从图中我们可以看到,创建就绪是一个单向的流转,即不可逆转,而 start 方法,正是将一个创建好了的线程状态标识为就绪状态,并加入到就绪队列中。处于就绪状态的线程,随时都可能被 CPU 调取执行。这就是源码要设计当我们多次对同一个线程调用 start() 时,抛出异常了。

image.png

02、结语

更多的线程状态内容,可以阅读菜鸟教程 - Java 线程的 5 种状态