如何在java中快速,干净的方式中止线程?
这是我的问题:我有一个对话框,用户可以改变一些参数(例如通过微调)。 每当这些参数中的一个发生变化时,我将启动一个线程根据新的参数值更新3D视图。 如果用户在第一个线程工作的时候改变了另外一个值(或者在微调箭头上多次点击了相同的值),我想中止第一个线程(和3D视图的更新)并启动一个新的线程用最新的参数值。
我怎么能这样做?
PS:在我的线程的run()
方法中没有循环,因此检查一个标记不是一个选项:更新3D视图的线程基本上只调用一个很长的方法来执行。 我不能添加任何标志在这个方法要求中止,因为我没有访问其代码。
尝试中断()有些人说,看看它是否有任何区别你的线程。 如果不是,请尝试销毁或closures将使线程停止的资源。 这有一个机会比尝试抛出Thread.stop()更好一点。
如果性能可以忍受,则可以将每个3D更新视为离散的不可中断事件,然后让它运行到结论,然后检查是否有最新的最新更新执行。 这可能会使用户对graphics用户界面有些不稳定,因为他们可以做出五个更改,然后从五个更改前的graphics结果中查看结果,然后查看最新更改的结果。 但取决于这个过程有多长时间,这可能是可以忍受的,它可以避免杀死线程。 devise可能如下所示:
boolean stopFlag = false; Object[] latestArgs = null; public void run() { while (!stopFlag) { if (latestArgs != null) { Object[] args = latestArgs; latestArgs = null; perform3dUpdate(args); } else { Thread.sleep(500); } } } public void endThread() { stopFlag = true; } public void updateSettings(Object[] args) { latestArgs = args; }
正在更新3D视图的线程应定期检查一些标志(使用volatile boolean
)以查看是否应该终止。 当你想中止线程,只需设置标志。 当线程接下来检查标志时,它应该简单地跳出正在使用的任何循环来更新视图并从其run
方法返回。
如果你真的不能访问线程正在运行的代码,让它检查一个标志,那么没有安全的方法来停止线程。 这个线程在你的应用程序完成之前是否正常终止? 如果是这样,是什么导致它停止?
如果它运行了很长一段时间,并且只需要结束它,就可以考虑使用不推荐使用的Thread.stop()
方法。 但是,有一个很好的理由已经被弃用了。 如果该线程在某些操作的中间停止,而某些操作会使某些状态不一致,或某些资源未正确清理,则可能会遇到麻烦。 以下是文档中的一个注释:
这种方法本质上是不安全的。 使用Thread.stop停止线程会导致它解锁所有已locking的监视器(作为未检查的ThreadDeathexception向上传播的自然结果)。 如果之前由这些监视器保护的任何对象处于不一致状态,则损坏的对象对其他线程变得可见,可能导致任意行为。 停止的许多用途应该被代码replace,该代码只是简单地修改一些variables来指示目标线程应该停止运行。 目标线程应该定期检查这个variables,并且如果variables指示它将停止运行,则从其run方法有序地返回。 如果目标线程长时间等待(例如,在条件variables上),则应使用中断方法来中断等待。 有关更多信息,请参阅Thread.stop,Thread.suspend和Thread.resume为什么不推荐使用?
为什么不使用已经在Java线程中的线程中断机制来代替滚动自己的布尔标志呢? 根据代码中的内部实现方式,你不能改变,你也许可以放弃它的一部分执行。
外螺纹:
if(oldThread.isRunning()) { oldThread.interrupt(); // Be careful if you're doing this in response to a user // action on the Event Thread // Blocking the Event Dispatch Thread in Java is BAD BAD BAD oldThread.join(); } oldThread = new Thread(someRunnable); oldThread.start();
内部可运行/线程:
public void run() { // If this is all you're doing, interrupts and boolean flags may not work callExternalMethod(args); } public void run() { while(!Thread.currentThread().isInterrupted) { // If you have multiple steps in here, check interrupted peridically and // abort the while loop cleanly } }
这是不是有点像问:“除了Thread.stop()以外的方法可用时,我怎样才能中止一个线程?
显然,唯一有效的答案是Thread.stop()。 它的丑陋,在某些情况下可能会破坏事物,会导致内存/资源泄漏,并被TLEJD(非凡Java开发者联盟)所诟病,但是在这种情况下仍然可能有用。 如果第三方代码没有可用的closures方法,那么确实没有其他的方法。
OTOH,有时候有后门closures的方法。 也就是说,closures其工作的底层stream,或者其他需要完成其工作的资源。 然而,这并不仅仅是调用Thread.stop()并让它经历一个ThreadDeathException。
一旦它的run()
方法完成,一个线程将会退出,所以你需要一些检查来完成这个方法。
您可以中断线程,然后进行一些检查,定期检查isInterrupted()
并返回run()
方法。
你也可以使用一个在线程中定期检查的布尔值,如果是这样的话,它会返回,或者如果线程正在做一些重复的任务,就把线程放到一个循环中,当你设置布尔值的时候它会退出run()方法。 例如,
static boolean shouldExit = false; Thread t = new Thread(new Runnable() { public void run() { while (!shouldExit) { // do stuff } } }).start();
不幸的是,杀死一个线程本质上是不安全的,因为使用可以通过锁来同步的资源的可能性,并且如果当前杀死的线程有一个锁可能导致程序进入死锁(不断地尝试获取无法获得的资源) 。 您将不得不手动检查是否需要从您要停止的线程中杀死。 易失性将确保检查variables的真实值,而不是以前可能存储的内容。 在一个侧面提示Thread.join在退出的线程上,以确保你等到死亡线程实际上没有做任何事情之前,而不是一直检查。
您似乎没有任何控制正在渲染屏幕的线程,但似乎有控制旋钮组件。 当线程正在渲染屏幕时,我将禁用微调器。 这样用户至less有一些与他们的行为有关的反馈。
我build议你通过使用wait和notify来防止多个线程,以便如果用户多次更改该值,它只会运行一次线程。 如果用户更改值10次,则会在第一次更改时触发线程,然后在完成线程之前所做的任何更改都会被“汇总”为一个通知。 这不会阻止一个线程,但没有好的方法来根据您的描述做到这一点。
旨在使用布尔型字段的解决scheme是正确的方向。 但是这个领域必须是不稳定的。 Java语言规范说 :
“例如,在以下(损坏的)代码段中,假设this.done是一个非易失性布尔值字段:
而(!this.done) 了Thread.sleep(1000);编译器可以自由地只读this.done一次,并在循环的每次执行中重用caching的值。 这意味着即使另一个线程改变了this.done的值,循环也不会终止。“
据我记得“实践中的Java并发”的目的是使用java.lang.Thread的interrupt()和interrupted()方法。
这个问题的接受答案允许你提交批处理作业到后台线程。 这可能是一个更好的模式:
public abstract class dispatcher<T> extends Thread { protected abstract void processItem(T work); private List<T> workItems = new ArrayList<T>(); private boolean stopping = false; public void submit(T work) { synchronized(workItems) { workItems.add(work); workItems.notify(); } } public void exit() { stopping = true; synchronized(workItems) { workItems.notifyAll(); } this.join(); } public void run() { while(!stopping) { T work; synchronized(workItems) { if (workItems.empty()) { workItems.wait(); continue; } work = workItems.remove(0); } this.processItem(work); } } }
要使用这个类,请扩展它,为T提供一个types,并为processItem()提供一个实现。 然后构造一个,并调用start()。
你可能会考虑添加一个abortPending方法:
public void abortPending() { synchronized(workItems) { workItems.clear(); } }
对于用户在渲染引擎之前跳过的情况,并且想要扔掉迄今为止已经安排的工作。
我在过去实现类似的方式是在我的Runnable
子类中实现一个shutdown()
方法,该方法将名为should_shutdown
的实例variablesshould_shutdown
为true。 run()
方法通常会在循环中执行某些操作,并会周期性地检查should_shutdown
以及何时返回true,返回或调用do_shutdown()
,然后返回。
您应该保持对当前工作线程的引用,并且当用户更改一个值时,在当前线程上调用shutdown()
,并等待它closures。 然后你可以启动一个新的线程。
我不会推荐使用Thread.stop
因为上次我检查时不推荐使用它。
编辑:
请阅读您的有关工作线程如何调用另一个需要一段时间才能运行的方法的注释,因此上述内容不适用。 在这种情况下,唯一真正的select是尝试调用interrupt()
并查看是否有任何影响。 如果不是,请考虑以某种方式手动导致您的工作线程正在调用的function中断。 例如,它听起来像是在做一些复杂的渲染,所以也许会破坏canvas并导致它抛出一个exception。 这不是一个很好的解决scheme,但据我所知,这是阻止这样的情况的唯一途径。
由于你正在处理的代码,你没有访问你可能运气不好。 标准程序(如其他答案中所述)是有一个由运行线程定期检查的标志。 如果标志已设置,请清理并退出。
由于该选项不适用于您,唯一的select是强制退出正在运行的进程。 这通过调用Thread.stop()来实现 ,但是由于以下原因(从javadocs复制) ,该方法已被永久弃用:
这种方法本质上是不安全的。 使用Thread.stop停止线程会导致它解锁所有已locking的监视器(作为未检查的ThreadDeathexception向上传播的自然结果)。 如果之前由这些监视器保护的任何对象处于不一致状态,则损坏的对象对其他线程变得可见,可能导致任意行为。
有关此主题的更多信息可以在这里find。
一个绝对可靠的方法可以完成你的请求(虽然这不是一个非常有效的方法),通过Runtime.exec()启动一个新的java进程,然后根据需要通过Process.destroy()停止该进程。 然而,像这样的进程之间共享状态并不是那么微不足道。
您是否考虑让线程观察您通过界面更改的属性,而不是使用线程启动和停止? 你会在某个时候仍然想要你的线程停止条件,但这也可以做到这一点。 如果你是MVC的粉丝,这很适合这种devise
对不起,在重新阅读你的问题后,这个和其他任何'检查variables'的build议都不能解决你的问题。
正确的答案是不使用线程。
你应该使用Executors,看看这个包:java.util.concurrent
也许这可以帮助你: 我们如何杀死Java中正在运行的线程?
你可以通过设置一个外部类variables来杀死一个特定的线程。
Class Outer { public static flag=true; Outer() { new Test().start(); } class Test extends Thread { public void run() { while(Outer.flag) { //do your work here } } } }
如果要停止上述线程,请将flagvariables设置为
false
。 杀死线程的另一种方法是在ThreadGroup中注册它,然后调用destroy()
。 这种方式也可以通过将它们创build为组或注册组来杀死类似的线程。