Java:控制线程速度/线程更改延迟

时间:2017-02-28 02:13:16

标签: java multithreading javafx

我需要一些java中的线程帮助。

我目前正在开发一个项目,它在运行时编译一个类并调用它的主要方法。该类表示区域中的人,该区域作为用户的画布可见。 此main方法调用其他一些方法。用户键入编辑器的方法或超类的预定义方法。 编辑器内容可能如下所示:

main() {
 System.out.println("test users class main");
 takeAll();
 takeAll();
}

public void takeAll() {
 for (int i = 0; i < 2; i++)
    move();
 takeHoney();
 takeHoney();
 takeHoney();
 for (int i = 0; i < 2; i++)
    move();
}

上面的代码是用户稍后进入GUI内部编辑器的代码,当他使用某个按钮时将对其进行编译。他应该学习命令式编程。

方法main,move和takeHoney在超类中定义,takeAll是用户在运行时定义的方法。 我的程序添加了类前缀并编译了用户类。

用户应该可以通过单击GUI中的某些按钮来启动,暂停,恢复和终止主方法。

当我运行main方法时,所有方法都完成得太快。用户只会看到结果,但不会看到步骤,并且在运行时无法进行互动。

到目前为止,我创建了一个新的runnable并启动了一个线程。

protagonistMainMethodRunnable = new Runnable() {
    @Override
    public void run() {
        protagonist.main();
        terminateWasPressed();
    }
};
//.....
Thread thread = new Thread(protagonistMainMethodRunnable);
thread.run();

&#34;主角&#34;是运行时编译的users类的实例。

我对线程不是很好,并且无法在每次方法调用后找到创建延迟的想法。

有人有想法在主要方法或每次方法调用之后的每个方法调用之后创建延迟吗?

编辑:James_D的想法非常有帮助。这是适用于我的用例的类:

public class OperationQueue {

    private Queue<Runnable> operationQueue = new LinkedList<Runnable>();
    private Timeline timeline;

    public OperationQueue(double delay) {
        timeline = null;
        setDelay(delay);
    }

    public void setDelay(double seconds) { //careful: seconds > 0
        if (timeline != null)
            timeline.stop();
        System.out.println("Set keyframe duration to " + seconds + "         seconds.");
        timeline = new Timeline(new KeyFrame(Duration.seconds(seconds), e -> {
            if (!operationQueue.isEmpty()) {
                operationQueue.remove().run();
            }       
        }));
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.play();
    }

    public Queue<Runnable> getOperationQueue() {
        return operationQueue;
    }

    public void add(Runnable queueItem) {
        this.operationQueue.add(queueItem);
    }
    public void clearQueue() {
        this.operationQueue.clear();
    }
}

我将时间轴移到了setter方法。通过这种方式,您可以在运行时更改延迟(例如,使用滑块)。

3 个答案:

答案 0 :(得分:1)

这只是我如何处理这个问题的概述,因为这个问题实在太宽泛了。

您真的在问如何执行用户在代码中定义的动画。 (这是一个动画,因为您正在显示一组帧,其中每个帧都是通过执行操作来定义的,并且操作之间存在时间差。)

考虑创建要执行的操作队列:

private class UI {

    private Queue<Runnable> operationQueue = new LinkedList<Runnable>();

    public Queue<Runnable> getOperationQueue() {
        return operationQueue();
    }

    // ...
}

现在你可以通过定期检查队列的Timeline运行动画,如果有什么内容,则执行该操作:

public UI () {

    // set up ui, etc...

    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), e-> {
        if (! operationQueue.isEmpty()) {
            operationQueue.remove().run();
        }
    }));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();
}

现在将预定义的基本方法设为私有,并定义将这些私有方法提交到队列的公共方法:

public class BaseClass {

    private final UI ui = ... ;

    private void doMove() {
        // implementation here...
    }

    public void move() {
        ui.getOperationQueue().add(this::doMove);
    }

    private void doTakeHoney() {
        // implementation here...
    }

    public void takeHoney() {
        ui.getOperationQueue().add(this::doTakeHoney);
    }
}

注意这里根本没有线程。一切都在FX应用程序线程上;时间由Timeline控制。

答案 1 :(得分:0)

您可以覆盖要运行缓慢的超类方法,例如move():

void move() throws Exception {
  Thread.sleep(1000);
  super.move();
  Thread.sleep(1000);
}

如果你想让你的代码可以通过UI按钮控制,那么你将添加一些开关。

答案 2 :(得分:0)

我认为您正在寻找的是javascript中所谓的setTimeout。不幸的是,Java没有这样的方法,你必须依靠Thread.sleep来模拟它。不幸的是,再次,在JavaFX应用程序线程上调用Thread.sleep将挂起应用程序,因此您需要使用JavaFX Timeline

您可以做的是声明一个可以为您自动执行此操作的界面:

class Delayed {
    private static long totalMillis;
    private static Timeline timeline = new Timeline();
    static void setTimeout(Runnable r, long millis) {
        timeline.getKeyFrames().add(new KeyFrame(new Duration(totalMillis += millis), event -> r.run()));
    }
    static void play() {
        timeline.play();
    }
}

方法调用

System.out.println("Hello World!");

然后变成:

Delayed.setTimeout(() ->System.out.println("Hello World!"), 1000);
Delayed.play();

我建议您使用您拥有的代码(用户输入的代码),并且当它仍然是String时,您将转换所有代码方法调用Delayed.setTimeout调用,如下所示:

final int TIMEOUT = 1000;
StringBuilder newCode = new StringBuilder();
Arrays.stream(codeString.split("\n")).forEach(s ->
{
    if (s.endsWith(";")) newCode.append("Delayed.setTimeout(() -> ")
            .append(s.substring(0, s.length() - 1)) //remove ";"
            .append(", ").append(TIMEOUT)
            .append(");")
            .append("\n");
    else {
        newCode.append(s + "\n");
    }
});
newCode.append("Delayed.play();");

当然,s.endsWith(";")检查非常幼稚,并且不会使用诸如if语句,do / while循环或多行语句之类的东西。我无法找到一种更有效的方法来检查一行是否是一个完整的语句,但这对于非常简单的代码(例如您似乎关注的代码)应该可以正常工作。

编辑现在正在使用JavaFX Application Thread。