【问题标题】:Java, replacement for infinite loops?Java,替代无限循环?
【发布时间】:2011-01-06 23:35:56
【问题描述】:

我正在制作一个通过数组显示细胞生长的程序。我已经知道了,所以当我按下开始按钮时,数组会在 while(true){} 循环中每 10 秒更新一次。问题是我希望能够通过按下暂停按钮来停止循环,但是在循环中,它不会让我使用任何控件。我需要除了无限循环之外的东西来刷新帧。

我是一个新手,但我目前正在学习 Java 课程。所以我掌握了一些语言。

【问题讨论】:

  • 开始和暂停按钮是什么意思?您的程序是否在某个 GUI 中运行?这是为您的班级设置的一些环境吗?

标签: java loops replace interruptions


【解决方案1】:

您需要的是使用 Timer 来更改组件的状态(在本例中为细胞生长),然后调用 JComponent.repaint()

这个定时器可以cancelled暂停然后重新启动,你只需新建一个:

所以你可以定义以下两种方法:

private Timer timer;
...
public void startPaiting() {
    timer = new Timer();
    timer.schedule( new TimerTask(){
        public void run(){
            changeState();
            repaint();
        }
    },0,  10000 ); // 10 s. 
}

public void pause(){
    timer.cancel();
}

然后在您的“暂停/恢复”按钮中调用此“暂停/启动”方法:

if( e.getActionCommand().equals("Pause")){
    growPanel.pause();
    setText("Resume");
} else {
    growPanel.startPaiting();
    setText("Pause");
}

这是查看它运行的完整源代码:

import javax.swing.*;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.*;
import java.util.Timer;
import java.util.TimerTask;

public class Grow {

    public static void main( String [] args ) {
        JFrame frame = new JFrame();
        final GrowPanel growPanel = new GrowPanel();
        frame.add( growPanel );
        frame.add( new JPanel(){{
            add( new JButton("Pause"){{
                addActionListener( new ActionListener(){
                    public void actionPerformed( ActionEvent e ){
                        if( e.getActionCommand().equals("Pause")){
                            growPanel.pause();
                            setText("Resume");
                        } else {
                            growPanel.startPaiting();
                            setText("Pause");
                        }
                    }
                });
        }});}}, java.awt.BorderLayout.SOUTH );
        frame.setSize( 400, 300 );
        frame.setVisible( true );
    }
}

class GrowPanel extends JComponent {
    private int x;
    private int y;
    private Timer timer;
    GrowPanel() {
        x = 10;
        y = 10;
        startPaiting();
    }

    public void startPaiting() {
        timer = new Timer();
        timer.schedule( new TimerTask(){
            public void run(){
                changeState();
                repaint();
            }
        },0,  100 ); // or 10000 which is 10 s. 
    }

    public void pause(){
        timer.cancel();
    }

    public void paintComponent( Graphics g ){
        g.fillOval( x, y, 10, 10 );
    }
    private void changeState(){
            x+=10;
            if( x >= 400 ) {
                y+=10;
                x = 0;
            }
            if( y >= 300 ){
                y = 10;
            }
    }

}

【讨论】:

  • 谢谢,这正是我需要的。我很难让它在我的代码中工作,因为它的设置与你的有点不同。但是会努力的谢谢
  • @Luke,我很高兴。您可以将我的答案标记为已接受(我告诉您这是因为您是 StackOverflow 的新手:P)
  • 哈哈,好吧。有什么办法可以把我的代码发给你吗?我在弄清楚如何将您给我的内容放入我的代码中时遇到了最糟糕的情况?我知道对陌生人有很多要求,所以不要担心太多。
  • 当然可以,但是,我认为如果您在这里创建一个新问题会更好。我可以看看那个或任何其他的。措辞如下:我问了这个 并且我遇到了这个问题以使其正常工作(描述您遇到的具体问题)。我想成为 Oscar 的解决方案,但我的代码不起作用。我的代码在这里 如果您有几个问题,请在每个问题上发布一个。我认为你会有更好的机会让它发挥作用。
【解决方案2】:

我建议使用单独的线程来处理数组。确保您使用的是线程安全对象(检查 Java Docs),并且当您想启动时,只需在线程对象上调用 .start() 即可。保留一个指向它的指针,以便您可以通过 setPaused(true) 暂停它

这样的……

class MyArrayUpdater extends Thread {
    private volatile boolean enabled = true;
    private volatile boolean paused = true;

    @Override
    public void run() {
        super.run();
        try {
            while (enabled) {
                if (!paused) {
                    // Do stuff to your array here.....
                }
                Thread.sleep(1);
            }
        } catch (InterruptedException ex) {
            // handle ex
        }
    }

    public void setEnabled(boolean arg) {
        enabled = arg;
    }

    public void setPaused(boolean arg) {
        paused = arg;
    }
}

【讨论】:

  • 由于 setPaused 和 setEnabled 应该从其他线程中调用,因此您应该使 enabled 和 paused 布尔值可变,或者在读取/设置它们时同步。
  • 如果应用程序暂停很长时间(例如1分钟)会消耗大量资源,因为while循环将全速运行,只是将线程设置为睡眠等待一毫秒,然后重新启动。这将创建 60,000 ( aprox ) 不需要的调用,仅停止 1 分钟。
  • 其实这是一个很差的解决方案。当暂停 == true 时,没有工作要做,此代码每 1 毫秒会产生两次上下文切换(读取:非常昂贵)(假设操作系统可以提供这样的准确性并且可以跟上)。最终结果是其他线程执行实际工作的速度减慢。更好的解决方案是等待,直到条件“paused == false”为真。使用老式锁和 Object#wait、#notify,或者通过 java.util.concurrent.locks.Lock/Condition 和适当的方法。奇怪的是,这个解决方案获得了最多的选票!
  • Paul Clapham 的回答要好得多。如果 SWT 没有类似的计时器事件,如果应用程序是在其中完成而不是摇摆,我会感到惊讶。
  • 等待时间或锁定可以有多种不同的处理方式。 sleep(1) 可以是可配置的,或者正如 Dimitris 所说,可以锁定直到#notify(我喜欢那个方法)。一般来说,上述方法是处理线程进程的一种简单而通用的方法。我也不认为下面基于计时器的解决方案是一个有效的解决方案,只要提问者使用 Swing 并且 1 秒更新对他来说足够快。
【解决方案3】:

如果这些“按钮”是 Swing 按钮,那么执行此操作的方法是:让“开始”按钮创建一个新的 javax.swing.Timer 对象,该对象每 10 秒更新一次。然后让暂停按钮停止该计时器。

【讨论】:

    【解决方案4】:

    您想在线程中运行模拟(查找可运行接口)。 然后您可以将消息传递给该线程以暂停、继续和停止。

    【讨论】:

      【解决方案5】:

      我个人更喜欢使用the Timer class 而不是ThreadThread.sleep()。计时器类处理定期运行代码和取消它。

      您的代码如下所示:

      TimerTask myTask = new TimerTask() {
        public void run() {
          // here goes my code
        }
      };
      
      Timer timer = new Timer();
      timer.schedule(myTask, 0 /*starts now*/, 10 * 1000 /* every 10 seconds */);
      
      // whenever you need to cancel it:
      timer.cancel();
      

      【讨论】:

        【解决方案6】:

        是的。听起来您正在事件调度程序线程上进行绘图。事件(按钮按下)永远没有机会被调用,因为从上次调用它起您从未返回控制权......

        【讨论】:

          【解决方案7】:

          您正在寻找的是多线程。将数据处理放在第二个线程中,异步运行,并使用主线程检查用户输入。

          当然,这也可能需要使用信号量在两个线程之间进行通信。您在哪个级别的课程中 - 您是否已经涵盖了这两个主题?

          【讨论】:

          • ahhhh,我在一个高层java类作为选择性,所以我的训练是......最小的。我不知道你在说什么,但我完全不知道如何将它实现到代码中。不过还是谢谢。
          【解决方案8】:

          看看 wait() / notify() 机制。简而言之,一个线程可以等待 10 秒,但仍然会被通知发生了外部事件,

          【讨论】:

            【解决方案9】:

            解决此问题的另一种方法是使用BlockingQueue of jobs。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-12-21
              • 2016-05-13
              • 1970-01-01
              • 2015-03-02
              • 2012-04-28
              • 1970-01-01
              • 2017-09-08
              相关资源
              最近更新 更多