在同一线程上两次调用start方法是否合法?


89

以下代码导致java.lang.IllegalThreadStateException: Thread already started第二次调用start()方法在程序中。

updateUI.join();    

if (!updateUI.isAlive()) 
    updateUI.start();

这是第二次发生updateUI.start()被调用。我已经遍历了好多次,并在点击之前调用了线程并完全运行完成updateUI.start()

呼唤 updateUI.run()可以避免该错误,但可以使线程在UI线程(如SO上其他文章所述的调用线程)中运行,这不是我想要的。

线程只能启动一次吗?如果是这样,那么如果我想再次运行线程该怎么办?如果我没有在线程中执行此特定线程,而不是在UI线程中完成该特定线程,则该线程在后台进行了计算,并且用户等待时间过长。


9
您为什么不阅读Javadoc-它清楚地描述了合同。
mP。

Answers:


111

根据Java API规范中的Thread.start方法:

一次启动一个线程永远是不合法的。特别是,线程一旦完成执行就可能不会重新启动。

此外:

抛出:
IllegalThreadStateException-如果线程已经启动。

所以是的,a Thread只能启动一次。

如果是,那么如果我想再次运行该线程该怎么办?

如果Thread需要多次运行,则应创建的新实例Thread并对其进行调用start


谢谢。我使用IDE和Java教程检查了文档中的线程(以及Google)。以后我会检查API规范。至关重要的“ ..永远不要合法启动多次..”不在其他解读中。

@coobird,如果我将旧线程对象名称分配给新的Thread(),则在旧线程完成之后,旧线程会被垃圾回收吗(即,它是自动回收的,还是必须显式完成)?
snapfractalpop 2012年

只要线程不再运行,它就会被垃圾回收。
2013年

1
这个答案有点过时了。如果现代Java程序需要执行一个以上的任务,则不应Thread每次都创建一个新的Java程序。相反,它应该将任务提交到线程池(例如java.util.concurrent.ThreadPoolExecutor
Solomon Slow

13

非常正确。 从文档中

一次启动一个线程永远是不合法的。特别是,线程一旦完成执行就可能不会重新启动。

在重复计算方面,似乎可以使用SwingUtilities invokeLater方法。您已经在尝试run()直接调用,这意味着您已经在考虑使用a Runnable而不是raw了Thread。尝试invokeLater仅在Runnable任务上使用该方法,然后看是否更适合您的心理模式。

这是文档中的示例:

 Runnable doHelloWorld = new Runnable() {
     public void run() {
         // Put your UI update computations in here.
         // BTW - remember to restrict Swing calls to the AWT Event thread.
         System.out.println("Hello World on " + Thread.currentThread());
     }
 };

 SwingUtilities.invokeLater(doHelloWorld);
 System.out.println("This might well be displayed before the other message.");

如果您更换 println用计算调用,则可能恰好是您所需要的。

编辑:评论之后,我没有在原始帖子中注意到Android标签。与Android工作中的invokeLater等效Handler.post(Runnable)。从其javadoc:

/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to which this handler is
 * attached.
 *
 * @param r The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */

因此,在Android世界中,您可以使用与上述相同的示例,将替换Swingutilities.invokeLater为相应的帖子到Handler


OP正在询问有关Android上的线程的信息,其中不包括SwingUtilities。
Austyn Mahoney 2010年

@奥斯汀,你是对的。我添加了有关Handler.post()的说明,以说明并行的Android代码。
鲍勃·克罗斯

1
如果您只是尝试更新UI,则另一种方法是使用RunOnUIThread(Runnable)View.post(Runnable)创建自己的Handler。这些将在主线程上运行runnable,从而允许您更新UI。
Austyn Mahoney 2010年

3

刚到的答案涵盖了为什么您不应该做自己在做的事情。这里有一些解决实际问题的方法。

如果我没有在线程中执行此特定线程,而不是在UI线程中完成该特定线程,则该线程在后台进行了计算,并且用户等待时间过长。

转储自己的线程并使用AsyncTask

或者在需要时创建一个新线程。

或者将线程设置为在工作队列之外运行(例如LinkedBlockingQueue),而不是重新启动线程。


3

,我们不能再次启动Thread,否则将引发runtimeException java.lang.IllegalThreadStateException。>

原因是一旦Thread执行run()方法,它将进入死状态。

让我们举一个例子-考虑再次启动线程并在其上调用start()方法(内部将调用run()方法)就像让死人醒来并运行一样。因为,在完成他的生命之后,人将进入死亡状态。

public class MyClass implements Runnable{

    @Override
    public void run() {
           System.out.println("in run() method, method completed.");
    }

    public static void main(String[] args) {
                  MyClass obj=new MyClass();            
        Thread thread1=new Thread(obj,"Thread-1");
        thread1.start();
        thread1.start(); //will throw java.lang.IllegalThreadStateException at runtime
    }

}

/ *在run()方法中输出,方法已完成。线程“主”中的异常java.lang.Thread.start上的java.lang.IllegalThreadStateException(未知源)* /

检查一下


2

您应该做的是创建一个Runnable,并在每次要运行Runnable时将其包装为新线程。这样做确实很丑陋,但是您可以将一个线程与另一个线程包装在一起以再次为其运行代码,但是只有这样做确实是必须的。


任何片段,如何包装?
Vinay

1

就像您说的那样,一个线程不能多次启动。

直指马:Java API规范

一次启动一个线程永远是不合法的。特别是,线程一旦完成执行就可能不会重新启动。

如果您需要重新运行线程中发生的一切,则必须创建一个新线程并运行它。


0

重用线程是Java API中的非法操作。但是,您可以将其包装到可运行的工具中,然后再次重新运行该实例。


0

是的,我们无法启动已经运行的线程。如果线程已经启动,它将在运行时引发IllegalThreadStateException。

如果您确实需要启动线程,该怎么办:选项1)如果一个线程需要运行多次,则应该创建该线程的新实例并对其调用start。


0

线程只能启动一次吗?

是。您可以只启动一次。

如果要重新运行该线程,该怎么办?如果特定线程在后台执行了一些计算,而不是在UI线程中执行了该操作,并且用户感到不合理,漫长的等待。

不要再运行了Thread。而是创建Runnable并将其发布到HandlerThread的Handler上。您可以提交多个对象。如果想将数据发送回UI线程,:在你的 方法,张贴在UI线程和进程RunnableRunnable run()MessageHandlerhandleMessage

请参阅此帖子以获取示例代码:

Android:在线程中吐司


0

我不知道这是否是一个好习惯,但是当我在run()方法中调用run()时,它不会抛出错误,并且实际上可以实现我想要的功能。

我知道它不会再次启动线程,但是也许这对您很方便。

public void run() {

    LifeCycleComponent lifeCycleComponent = new LifeCycleComponent();

    try {
        NetworkState firstState = lifeCycleComponent.getCurrentNetworkState();
        Thread.sleep(5000);
        if (firstState != lifeCycleComponent.getCurrentNetworkState()) {
            System.out.println("{There was a NetworkState change!}");
            run();
        } else {
            run();
        }
    } catch (SocketException | InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Thread checkingNetworkStates = new Thread(new LifeCycleComponent());
    checkingNetworkStates.start();
}

希望这会有所帮助,即使只是一点点。

干杯


-1

这样做确实很丑陋,但是您可以将一个线程与另一个线程包装在一起以再次为其运行代码,但是只有这样做确实是必须的。

我不得不修复由程序员创建的线程导致的资源泄漏,但是他没有调用start(),而是直接调用run()方法。因此,请避免使用它,除非您真的真的知道它会导致什么副作用。


但是建议不要run()直接调用,而只是将Runnable嵌入线程中并大概调用start()
H2ONaCl

@ H2ONaCl如果您阅读了我引用的文字,则建议将一个线程包装在一个线程中。可能是您在编辑原始建议之前没有阅读过。
Torben
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.