【问题标题】:Alternative to Thread.suspend() and .resume()替代 Thread.suspend() 和 .resume()
【发布时间】:2019-11-07 04:53:25
【问题描述】:

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

【问题讨论】:

  • 你是如何执行每个命令的?如果那是像“Action”或“Command”这样的单独类,您可以在那里执行检查。除此之外,您无能为力...如其他答案中所述。
  • 我是 FRC 机器人的自主代码。其中一些是设置电机的速度并等待,其他位是设置电机值,直到传感器给出一定的值。

标签: java multithreading


【解决方案1】:

我可以检查每一行代码之间的布尔值,但我希望有一个更优雅的解决方案。有什么好办法吗?

很遗憾,没有。

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

按照目前的规范和实现,这在 Java 中是不可能安全地做到的。

不推荐使用的 Thread 方法是一个线程杀死、停止/暂停、恢复另一个线程的唯一方法......如果另一个线程没有积极合作。它们已被弃用,因为(简而言之)这种控制无法在任何当代主流 JVM1 中安全实现。

你能做的最好的就是将暂停/恢复逻辑包装在第二个线程在适当点调用的方法中。 (这可能应该使用 wait / notify ... 或等效的 ... 来处理恢复。但是 wait/notify 本身并没有解决您对暂停/恢复非合作线程的明确要求.)


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

【讨论】:

【解决方案2】:

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

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

【讨论】:

  • 我不同意这一点。中断的问题是它会......基本上......中断一些阻塞操作,使被中断的线程处于恢复(至少)复杂的状态。测试布尔值是暂停/恢复的更好方法。
  • @StephenC 所有可能被中断的阻塞操作都会迫使您检查 InterruptedException 并决定要做什么。在我看来,如果他们正在使用任何此类操作,那么打断它们是期望的行为(尽管他们可能需要添加一些信号手段来不增加 taskPos);如果这不是所需的行为,那么他们可以轻松地将Thread.interrupted() 替换为特殊字段查询。
  • 在这种情况下,OP 想要挂起/恢复,而不是中断/重新开始语义。问题说他正在寻找 Thread.suspend() 等的替代品。
  • 我喜欢这个解决方案,因为它通过回退克服了在暂停/暂停任务之间可能出现的问题(例如,考虑暂停的排序算法,同时底层列表发生变化)到最后一个明确的状态。当然,这可能会导致其他问题,尤其是当被中断的任务已经执行了一些最好撤消的状态更改时。因此,如果撤消更改过于复杂,任务可能会决定不对中断做出反应。它拥有完全的控制权!比使用已弃用的线程方法要好得多!
【解决方案3】:

关于为什么不推荐使用 Thread.stop()Thread.pause()Thread.resume() 有一个很好的文档:

Java Thread Primitive Deprecation

还解释了Thread.pause() / Thread.resume() 使用waitnotify 的替代方法。

【讨论】:

  • 我想不出在我的情况下如何使用等待和通知。如果可以的话,我很高兴知道。
  • @yesennes 查看答案stackoverflow.com/questions/28922040/… 使用等待/通知来执行此操作。
【解决方案4】:

您可以使用等待/通知作为挂起/恢复的替代方法。您可以设置标志,通知并从通知线程抛出异常,而不是停止

【讨论】:

  • 记住这不是一段循环的代码。不方便经常检查标志的地方。如果我理解您的解决方案,我会每隔一行检查一次标志,此时我不妨只检查布尔值。
【解决方案5】:

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

我在下面的例子中使用了 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

【讨论】:

    【解决方案6】:

    我不知道为什么这里没有人提到 sephamore。您可以每隔一段时间尝试在主代码中获取和释放一个 sephamore。在辅助工作中,您可以触发 sephamore 获取,如果您不希望整个 while 循环等待并通知继续,则可以阻止主代码获取。

    【讨论】:

      【解决方案7】:

      不知何故,这个帖子似乎仍然活跃,所以我会提出我的答案,因为 4 年的开发经验让我有了更多的洞察力,并没有完全模糊我对问题的记忆。

      问题归结为我使用的 FRC 库试图强迫我使用大致类似于:motor.drive(speed, time) 的函数,并且会在一段时间内阻止执行。除了最狭窄的情况外,在所有情况下,这都是不好用的函数,因为很多机器人代码依赖于结合传感器数据和时间传递来做出决策。

      我应该做的是调用它,时间为0或非常小,或者找到一些其他可以无限期驱动电机的函数,然后确保将其关闭。然后在一个紧密的循环中运行该函数,寻找可以转到下一个命令的条件。

      我可能应该使用状态机来使代码更具可读性和更容易修改。

      【讨论】:

        猜你喜欢
        • 2020-05-26
        • 1970-01-01
        • 2012-10-31
        • 2011-06-17
        • 1970-01-01
        • 1970-01-01
        • 2011-02-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多