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

Posted

技术标签:

【中文标题】在同一个线程上调用 start 方法两次是不是合法?【英文标题】:Is it legal to call the start method twice on the same Thread?在同一个线程上调用 start 方法两次是否合法? 【发布时间】:2010-11-15 23:28:23 【问题描述】:

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

updateUI.join();    

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

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

调用updateUI.run() 可以避免错误,但会导致线程在 UI 线程中运行(调用线程,如 SO 上的其他帖子中所述),这不是我想要的。

一个线程可以启动一次吗?如果是这样,如果我想再次运行线程该怎么办?这个特定的线程正在后台进行一些计算,如果我不在线程中进行计算,而不是在 UI 线程中进行计算,并且用户等待的时间过长。

【问题讨论】:

你为什么不直接阅读 javadoc - 它清楚地描述了合同。 【参考方案1】:

来自Java API Specification 的Thread.start 方法:

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

此外:

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

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

如果是这样,如果我愿意,我该怎么做 再次运行线程?

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

【讨论】:

谢谢。我使用 IDE 和 Java 线程教程(以及谷歌)检查了文档。将来我会检查 API 规范。关键的“..从不合法开始不止一次..”不在其他阅读中。 @coobird,如果我将旧线程对象名称分配给新线程(),在旧线程完成后,旧线程是否会被垃圾回收(即它是自动回收,还是必须这样明确地完成)? 只要线程不再运行,就会被垃圾回收。 这个答案有点过时了。如果现代 Java 程序需要多次执行任务,那么它不应该每次都创建一个新的Thread。相反,它应该将任务提交到 线程池(例如,java.util.concurrent.ThreadPoolExecutor【参考方案2】:

完全正确。 From the documentation:

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

就重复计算可以做什么而言,似乎可以使用SwingUtilities invokeLater method。您已经在尝试直接调用run(),这意味着您已经在考虑使用Runnable 而不是原始Thread。尝试仅对 Runnable 任务使用 invokeLater 方法,看看这是否更符合您的思维模式。

这是文档中的示例:

 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,你是对的。我添加了关于 Handler.post() 的注释来说明并行的 Android 代码。 如果您只是想更新您的 UI,另一种方法是使用 RunOnUIThread(Runnable)View.post(Runnable) 而不是创建自己的处理程序。这些将在主线程上运行 runnable,允许您更新 UI。【参考方案3】:

刚刚得到的答案涵盖了为什么你不应该做你正在做的事情。这里有一些选项可以解决您的实际问题。

这个特定的线程正在做一些事情 在后台计算,如果我 不要在线程中这样做 在 UI 线程中完成并且用户有 等待时间过长。

转储你自己的线程并使用AsyncTask

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

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

【讨论】:

【参考方案4】:

,我们不能再次启动 Thread,这样做会抛出 runtimeException java.lang.IllegalThreadStateException。 >

原因是线程一旦执行了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
    


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

check this

【讨论】:

【参考方案5】:

你应该做的是创建一个 Runnable 并在每次你想运行 Runnable 时用一个新的 Thread 包装它。 这样做真的很难看,但你可以用另一个线程包裹一个线程来再次运行它的代码,但只有这样做是你真正必须要做的。

【讨论】:

任何片段,如何包装?【参考方案6】:

正如你所说,一个线程不能多次启动。

直接从马嘴里:Java API Spec

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

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

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

是的,我们不能启动已经运行的线程。 它将在运行时抛出 IllegalThreadStateException - 如果线程已经启动。

如果你真的需要启动线程怎么办: 选项 1 ) 如果一个线程需要运行不止一次,那么应该创建一个线程的新实例并在其上调用 start。

【讨论】:

【参考方案9】:

一个线程只能启动一次吗?

是的。您可以只启动一次。

如果是这样,如果我想再次运行线程,我该怎么做?这个特定的线程正在后台进行一些计算,如果我不在线程中执行它而不是在 UI 线程和用户中执行等待时间过长。

不要再次运行Thread。而是创建Runnable 并将其发布在Handler 的HandlerThread 上。您可以提交多个Runnable 对象。如果想将数据发送回 UI 线程,在您的 Runnable run() 方法中,在 UI 线程的 Handler 上发布 Message 并处理 handleMessage

示例代码参考这篇文章:

Android: Toast in a thread

【讨论】:

【参考方案10】:

这样做真的很难看,但你可以用另一个线程包裹一个线程来再次运行它的代码,但只有你真的必须这样做。

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

【讨论】:

但建议不要直接调用run(),而只是在线程中嵌入一个Runnable并大概调用start() @H2ONaCl 如果您阅读我引用的文本,建议将线程包装在线程中。可能是您在编辑之前没有阅读原始建议。【参考方案11】:

我不知道这是否是一个好习惯,但是当我在 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();

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

干杯

【讨论】:

以上是关于在同一个线程上调用 start 方法两次是不是合法?的主要内容,如果未能解决你的问题,请参考以下文章

Java线程基础面试大全

为啥 Application_Start 从不同的线程调用两次?

java线程只能被启动(Thread.start())一次,那么为啥线程池中的线程能被重复利用呢?

块在异步线程上执行两次

java中多线程执行时,为何调用的是start()方法而不是run()方法

Java - 两次运行线程[关闭]