在同一个线程上调用 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 方法两次是不是合法?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Application_Start 从不同的线程调用两次?
java线程只能被启动(Thread.start())一次,那么为啥线程池中的线程能被重复利用呢?