同一个线程多次调用start()会出现的问题

简介: 同一个线程多次调用start()会出现的问题

测试代码:


package com.led.test;
/**
 * @author Alan
 * @date 2018/6/18 15:09
 * @description 测试同一个线程多次调用start()方法
 */
public class MuchStart implements Runnable{ //实现Runnable接口
    public void run() {
        System.out.println(Thread.currentThread().getName() + "is running ...");
    }
    public static void main(String[] args) {
        MuchStart muchStart = new MuchStart();
        Thread t = new Thread(muchStart);//创建实现了Runnable接口的Thread实例
        t.start();//多次调用start()方法
        t.start();
        t.start();
    }
}


测试结果:


  线程首先会运行一次,然后抛出java.lang.IllegalThreadStateException异常。



接下来深入源码分析下原因:


  我们根据控制台的异常信息,定位到Thread.java的第708行,也就start()方法内部,打个断点调试:


 

  调试发现,第一个次运行start()方法时,threadStatus是0,此时if条件不满足,继续执行,会将当前线程添加到线程组中去执行。第二次运行start()方法时,threadStatus变成了2,if条件满足,于是抛出了java.lang.IllegalThreadStateException异常。





Thread-0is running ...
Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)
    at com.led.test.MuchStart.main(MuchStart.java:17)


start()源码进行分析:


/**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.      让这个线程开始执行。JVM会调用这个线程的run()方法。
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.一个线程多次调用start()方法是非法的。
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *特别说明:一个线程执行完后,不太可能重新运行。
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.              如果该线程已经启动,则再次调用start()方法,就会抛出IllegalThreadStateException异常。
     * @see        #run()
     * @see        #stop()
     */
    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)//新的线程threadState值是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);//通知线程组该线程将要开始运行,这样该线程就会被添加到线程列表中,此时列表的unstarted数将会减少。
        boolean started = false;
        try {
            start0();//调用原生方法态方法启动线程
            started = true;//已经运行的标记设置为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 */
            }
        }
    }


group.add(this)  ---线程组添加线程源码分析:


/**
     * Adds the specified thread to this thread group.
     * 添加特定的线程到该线程组
     * <p> Note: This method is called from both library code
     * and the Virtual Machine. It is called from VM to add
     * certain system threads to the system thread group.
     *
     * @param  t
     *         the Thread to be added
     *
     * @throws  IllegalThreadStateException
     *          if the Thread group has been destroyed
     */
    void add(Thread t) {
        synchronized (this) {//线程同步
            if (destroyed) {//如果线程组的销毁标记(destroyed)是true,则抛出IllegalThreadStateException
                throw new IllegalThreadStateException();
            }
            if (threads == null) {//如果线程数组为空,则初始为4个新的线程
                threads = new Thread[4];
            } else if (nthreads == threads.length) {//如果当前线程组已满,则扩容至原来的2倍
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;//将当前线程放入线程数组的末尾
            // This is done last so it doesn't matter in case the
            // thread is killed
            nthreads++;//线程数组元素个数+1
            // The thread is now a fully fledged member of the group, even
            // though it may, or may not, have been started yet. It will prevent
            // the group from being destroyed so the unstarted Threads count is
            // decremented.
            nUnstartedThreads--;//未启动线程数-1
        }
    }


start0()代码分析:


private native void start0();//原生态代码,源码不在当前文件中,调用的其它文件的代码(可能是c/c++写的代码)


threadStartFailed()源码分析:


/**
     * Notifies the group that the thread {@code t} has failed
     * an attempt to start.
     * 通知线程组该线程尝试运行失败
     * <p> The state of this thread group is rolled back as if the
     * attempt to start the thread has never occurred. The thread is again
     * considered an unstarted member of the thread group, and a subsequent
     * attempt to start the thread is permitted.
     *
     * @param  t
     *         the Thread whose start method was invoked
     */
    void threadStartFailed(Thread t) {
        synchronized(this) {//同步
            remove(t);//从线程组中移除该线程
            nUnstartedThreads++;//未启动线程数+1
        }
    }


总结:


  同一个线程只能调用start()方法一次,多次调用会抛出java.lang.IllegalThreadStateException。启动一个线程,需要调用start()方法而不是run()方法。此时,当前线程会被添加到线程组中,进入就绪状态,等待线程调度器的调用,若获取到了资源,则能进入运行状态,run()方法只是线程体,即线程执行的内容,若没调用start()方法,run()方法只是一个普通的方法。

相关文章
|
2月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
【多线程面试题 三】、 run()和start()有什么区别?
run()方法定义线程执行的任务,而start()方法启动线程,使得run()在新的线程中异步执行;直接调用run()方法只会同步执行run()中的代码,不会创建新线程。
|
6月前
|
安全 Java
【JAVA】线程的run()和start()有什么区别?
【JAVA】线程的run()和start()有什么区别?
|
6月前
|
安全 Java
面试官:线程调用2次start会怎样?我支支吾吾没答上来
面试官:线程调用2次start会怎样?我支支吾吾没答上来
32 1
|
Java 调度
start()方法和run()方法区别与多线程抢占式运行原理
我们通过一个例子来进行总结,我们写一个利用Thread创建的简单的多线程例子,然后分别执行start()与run()方法,执行结果如下所示:
91 0
|
6月前
|
Java
多线程中的run方法和start方法有什么区别?
多线程中的run方法和start方法有什么区别?
|
安全 Java API
为什么启动线程不直接调用run(),而要调用start(),如果调用两次start()方法会有什么后果
1位工作6年的小伙伴去某里P6一面,被问到这样一道面试题,说,为什么启动一个线程不直接调用run()方法,而要调用start()方法来启动,如果调用两次start()会有什么后果?
114 0
|
Java
Java线程中的run()和start()区别
Java线程中的run()和start()区别
86 0
|
存储 Java 编译器
进程和线程的区别&&run和start区别与联系
进程和线程的区别&&run和start区别与联系
|
安全 Java
【Java|多线程与高并发】详解start()方法和run()方法的区别
本篇文章主要讲解以下几个问题:start()方法和run()方法的区别与联系 为什么不能连续两次调用shart()方法 run()方法中可能忽略的问题