Thread.suspend()和.resume()的替代方案

我有一大段代码不是循环,只是一个发生一次但需要一些时间的命令列表。 我需要它根据更改的布尔值在任何点暂停或终止它。 我可以使用不同的线程来暂停,恢复和停止此代码,但这些方法已被弃用,所以我想避免使用它们。 我可以检查每行代码之间的布尔值,但我希望有一个更优雅的解决方案。 有没有一个很好的方法来做到这一点?

处理中断线程的正确方法(在这种情况下,暂停或停止它)当然是使用Thread#interrupt() 。 它的设计使您可以定义线程可以被中断的安全点,这对您来说自然就是每个任务之间的点。 因此,为了避免在每个任务之间手动检查变量,并且能够轻松地从中断的位置恢复,您可以将任务存储为Runnable列表,并记住从上次停止时列表中的位置, 像这样:

 public class Foo { public static void runTask(Runnable task) throws InterruptedException { task.run(); if (Thread.interrupted()) throw new InterruptedException(); } Runnable[] frobnicateTasks = new Runnable[] { () -> { System.out.println("task1"); }, () -> { Thread.currentThread().interrupt(); }, //Interrupt self only as example () -> { System.out.println("task2"); } }; public int frobnicate() { return resumeFrobnicate(0); } public int resumeFrobnicate(int taskPos) { try { while (taskPos < frobnicateTasks.length) runTask(frobnicateTasks[taskPos++]); } catch (InterruptedException ex) { } if (taskPos == frobnicateTasks.length) { return -1; //done } return taskPos; } public static void main(String[] args) { Foo foo = new Foo(); int progress = foo.frobnicate(); while (progress != -1) { System.out.println("Paused"); progress = foo.resumeFrobnicate(progress); } System.out.println("Done"); } } --> task1 Paused task2 Done 

我可以检查每行代码之间的布尔值,但我希望有一个更优雅的解决方案。 有没有一个很好的方法来做到这一点?

很不幸的是,不行。

要替换暂停/恢复,你真正需要的是一个线程“暂停”另一个线程,而不涉及第二个线程的代码。

这在当前规范和实现的Java中是不可能安全的。

不推荐使用的Thread方法是一个线程杀死,停止/暂停,恢复另一个线程的唯一方法……如果另一个线程没有主动协作。 它们被弃用了,因为(简而言之)在任何当前的主流JVM 1中 都无法安全地实现这种控制。

您可以做的最好的事情是将暂停/恢复逻辑包装在第二个线程在适当的位置调用的方法中。 (这可能应该使用wait / notify …或等效的…来处理恢复。但是wait / notify 本身并不能解决你暂停/恢复非协作线程的明确要求。)


1 – 你需要像Isolates API这样的东西。 已经尝试在研究项目中实现它,但AFAIK Isolates从未在生产JVM中实现。

您可以使用wait / notify作为暂停/恢复的替代方法。 您可以设置标志,通知并从通知的线程中抛出exception,而不是停止

有一个很好的文档,说明为什么不推荐使用Thread.stop()Thread.pause()Thread.resume()

Java Thread Primitive Deprecation

还有一个替代Thread.pause() / Thread.resume()使用waitnotify解释。

我知道如何使用AspectJ来做到这一点。 我在下面概述的解决方案将允许您暂停/恢复另一个线程。 此暂停仅在您调用暂停例程后从目标方法内部调用下一个方法时生效。 在目标方法的每行代码之后,这不需要布尔检查。 我正在使用wait / notify来执行此操作。

我在下面的例子中使用Spring。

首先,将目标方法包装在Runnable中。

  package com.app.inter.thread.communication.runnables; public class TestRunnable implements Runnable{ public void run() { long i=0; //For explanatory purposes, I have used an infinite loop below //and printed an increasing number while(!Thread.interrupted()){ //someService.action1(); //someService.action2(); //.... if(i++%10000000==0) System.out.println("Working "+i); } } } 

接下来,您将创建一个方面,该方面将在目标方法中的任何方法调用上有条件地调用。

 @Aspect @Component public class HandlerAspect { public static volatile boolean stop=false; public static volatile String monitor=""; @Pointcut("withincode(public void com.app.inter.thread.communication.runnables.TestRunnable.run()) " + "&& call(* *.*(*)) " + "&& if()") public static boolean stopAspect(){ return stop; } @Before("stopAspect()") public void beforeAdvice(JoinPoint jp) { try { System.out.println("Waiting"); synchronized(monitor){ monitor.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } stop=false; } } 

现在,只要你想暂停你的线程,只需将stop的值设置为true,反之亦然。

 @Service public class Driver { @Autowired private HandlerAspect aspect; @PostConstruct public void init(){ Scanner scanner = new Scanner(System.in); int i=-1; TestRunnable runnable = new TestRunnable(); Thread thread= new Thread(runnable); thread.start(); aspect.monitor="MONITOR"; while((i=scanner.nextInt())!=0){ switch(i){ case 1: aspect.stop=true; break; case 2: aspect.stop=false; synchronized(aspect.monitor){ aspect.monitor.notify(); } break; } } // in case the main thread is stopped // while the other thread is in wait state synchronized(aspect.monitor){ aspect.monitor.notify(); } thread.interrupt(); } } 

输出如下:

 ... Working 400000001 1Working 410000001 Working 420000001 Working 430000001 Working 440000001 Working 450000001 Working 460000001 Working 470000001 Waiting 2 Working 480000001 Working 490000001 0 

工作代码位于https://github.com/zafar142007/InterThreadCommunication