使用Timer在固定时间重绘,然后继续计算

时间:2017-06-05 15:43:17

标签: java multithreading swing timer visualization

目前,我正在为查找最小生成树的图形可视化Prim算法制作Java程序。

Here is the image of my program's output

while(condition){
    //Find the min vertex and min edges
    Vertex vertex = findMinVertex();
    Edge[] edges = findMinEdges();

    //Then, for each vertex and edges I found, I will change the color of 
    //them and pause the program for 3 seconds, so user can see how 
    //algorithm works.

    repaintAndPause(3000);
}
.
.
private void repaintAndPause(int time){
    long start = System.currentTimeMillis();
    long end = start + speed;

    //Here is the timer for repainting.
    Timer timer = new Timer(speed, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e){
            GraphPanel.this.repaint();
        }
    });
    timer.setRepeats(false);
    timer.setDelay(0);
    timer.start();

    //And here is for pausing the program, a while loop without any commands.
    while(System.currentTimeMillis() < end){}
}

但是,我不知道为什么,但程序不起作用。是的,有程序的暂停,但是所有的边和顶点都只是改变了程序结束时的颜色。它们不会每3秒钟更换一次。

有人可以告诉我哪里做错了吗?

谢谢你,希望你过得愉快!

1 个答案:

答案 0 :(得分:1)

  

有人可以告诉我哪里做错了吗?

是。您正在Event Dispatching Thread中放置一个忙循环。

    while(System.currentTimeMillis() < end){}

您的代码为:

  1. 进行一些计算(忙碌
  2. 完成后,发布“重新粉饰”消息,重绘面板不忙时
  3. 继续非常忙碌 3秒内无所事事
  4. 重复步骤1到步骤4继续忙碌
  5. while (condition)循环结束后,事件调度线程永远不会完成处理第一个“事件”直到算法结束。

    你想:

    Timer timer = new Timer(speed, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
           /* Code to perform one step of Prim's algorithm here */
           /* Change edge/vertex colour for step */
           /* Call timer.stop(), once done */
    
           GraphPanel.this.repaint();
        }
    });
    timer.setRepeats(true);
    timer.setDelay(3000);
    timer.start();
    

    在计时器的每个刻度线上(每3秒一次),执行算法的一步。

    注意这意味着算法的每一步都必须运行,并将任何部分结果存储到类成员中,因此下一步将能够检索它需要继续的所有信息。堆栈变量只能在一个步骤内使用;它们不能用于保持步骤间值。

    您可以重新编写算法以使用SwingWorker在其自己的后台线程中运行计算,并在计算时使用publish中间结果。然后,EDT可以重新绘制,因为产生了中间结果。通过Thread#sleep()调用,此后台线程可以将中间结果的生成延迟到每3秒一次。

    或者,您可以运行算法,并为每个“步骤”存储一次输出的多个副本。然后你的Panel计时器可以简单地显示下一步的输出。