【问题标题】:Call function every x time without blocking the GUI Java在不阻塞 GUI Java 的情况下每 x 次调用一次函数
【发布时间】:2016-03-18 08:12:45
【问题描述】:

我有一个名为 ItemGUI 的类,它处理与用户界面相关的所有内容。用户可以添加一些链接,即项目,因此当他插入链接并单击“添加”按钮时,它应该创建一个 Item 类的新对象并开始运行名为 getPrice() 的函数,类似于那:

Item newItem = new Item(newItemField.getText());
// should also be added to a list of items which should be in the ItemGUI class
newItem.getPrice()

这应该在单击添加按钮后完成。然后我将项目打印到桌子上。问题是 getPrice() 方法应该每 5 秒运行一次而不会阻塞我的 GUI,所以我应该实现 Threads。

我的问题是:我如何才能实现一个线程,该线程每 5 秒运行一次该函数(对于列表中的每个项目),直到我单击停止按钮?我正在考虑使用带有时钟的观察者可观察类,该时钟每 5 秒通知一次观察者。这会是最好的选择吗? 另外,我能否从 ItemGUI 类中检索项目变量?

谢谢!

【问题讨论】:

  • 如果你使用 Swing,你应该使用 SwingWorker 或 Swing Timer

标签: java multithreading observable


【解决方案1】:

首先,我不知道该怎么做应该,但我想我有一个关于如何让它发挥作用的建议。 在我正在处理的一个项目中,我使用 ExecutorService 来处理我的线程池。我必须在这里提一下,我自己一个多星期前就开始使用 Java 中的线程,所以如果我的建议过于基本或错误,我深表歉意。

我的建议是,您可以创建一个可用于控制循环的静态布尔变量。代码可能如下所示:

public static boolean isRunning;

public static void main(String[] args) {

    ExecutorService executerService = Executors.newCachedThreadPool();

    executerService.execute(new Runnable() {
        @Override
        public void run() {
            isRunning=true;
            while (isRunning){
                System.out.println("hello world"); //Your loop here
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

当你将 isRunning 布尔值设置为 false 时,循环将停止,线程将在片刻后结束。

至于观察者可观察的课程,我不知道这一点,我不确定我是否理解你的最后一个问题。

【讨论】:

  • 感谢您的回答。使用观察者模式,我会像你一样做一些事情。我将创建一个名为 Clock 的新类(它将扩展/实现线程),并且我将有一个函数可以在您执行此操作时执行此操作。然后我会添加观察者,在 sleep(5000) 完成后,他们将收到通知并执行特定函数,在我的情况下,观察者将是执行getPrice() 的项目和函数。我不知道如何使用您提供的解决方案为每个项目执行 getPrice() 函数,因为我从未使用过 ExecutorService
  • 感谢您回复我的回答 :) Executors.newCachedThreadPool() 是 ExecutorService 的实现,它在线程池中为您管理线程。因此,如果在您调用 .execute() 命令时没有可用线程,则此实现将生成线程。执行命令将 Runnable 作为参数,如果您的 GUI 是用 JavaFX 制作的,它可能是 Task 类的实例?至于“每个项目”部分,您是否将所有项目都放在 ArrayList 中?你可以做一个增强的 for 循环:for(Item i : yourarraylist)
【解决方案2】:

您可以使用Timer 来安排重复任务。

与每个 Timer 对象对应的是一个单独的后台线程,用于按顺序执行计时器的所有任务

类似这段代码的东西应该可以解决问题。

Timer timer = new Timer();
TimerTask task = new TimerTask(){
    public void run() {
        getPrice(); //your task
    }
};

timer.schedule(task, 0, 5000); //first is delay, second is repeat period

...
// on button click, simple cancel the task
task.cancel()

【讨论】:

  • 我应该在哪里声明 Timer 对象,在 ItemGUI 内还是在每个项目内?我应该为每个项目设置一项任务吗?
  • 我会将计时器声明放在 GUI 类中,因为您只需要创建一个(尽管垃圾收集可以处理许多计时器)。但是每次都必须重新创建任务,因为一旦取消任务就无法重新安排它。
【解决方案3】:

更新

MadProgrammer 建议最明确的解决方案是使用swing Timers,如下所示:

protected javax.swing.Timer refresherTimer = null;
protected void stopRefreshing() {
    if (refresherTimer != null) {
        refresherTimer.stop();
        refresherTimer = null;
    }
}
protected void startRefreshing() {
    stopRefreshing();
    refresherTimer = new Timer(500, e -> {
        newItem.getPrice()
    });
    refresherTimer.start();
}
public void onStartButtonClicked() {
    Item newItem = new Item(newItemField.getText());
    // here newItem should be added to a list of items which should be in the ItemGUI class
    startRefreshing();
}
public void onStopButtonClicked() {
    stopRefreshing();
}

原答案

如果有一些名为 e.g. 的实用程序会很好。 GuiTimer 这将使您的任务变得如此简单:

protected GuiThread.Task refresherTask = null;
protected void cancelRefreshing() {
    if (refresherTask != null) {
        refresherTask.cancel();
        refresherTask = null;
    }
}
public void onStartButtonClicked() {
    Item newItem = new Item(newItemField.getText());
    // should also be added to a list of items which should be in the ItemGUI class
    cancelRefreshing();
    refresherTask = GuiThread.scheduleAtFixedRate(() -> {
        newItem.getPrice()
    }, 0, 5, TimeUnit.SECONDS);
}
public void onStopButtonClicked() {
    cancelRefreshing();
}

常规定时器的问题是它们在自己的线程上调用回调函数,而不是在 gui 线程上,因此它需要开发人员确保正确的线程。不幸的是,内置的 java EventQueue 不支持调度延迟任务。

出于这个原因,我喜欢使用名为 GuiTimer 的以下实用程序,它将充当纯 gui 线程计时器:

public class GuiTimer {
    public static final ScheduledThreadPoolExecutor executor = 
            new ScheduledThreadPoolExecutor(1);

    public static interface Task {
        public void cancel();
    }

    private static class CancelStateTask implements Task {
        public volatile boolean canceled = false;

        @Override
        public void cancel() {
            this.canceled = true;
        }
    }

    public static Task schedule(final Runnable action) {
        CancelStateTask task = new CancelStateTask();
        EventQueue.invokeLater(() -> {
            if (!task.canceled)
                action.run();
        });
        return task;
    }

    public static Task schedule(final Runnable command, long delay,
            TimeUnit unit) {
        ScheduledFuture<?> future = executor.schedule(
                () -> EventQueue.invokeLater(command), delay, unit);
        return () -> future.cancel(false);
    }

    public static Task scheduleAtFixedRate(Runnable command,
            long initialDelay, long period, TimeUnit unit) {
        ScheduledFuture<?> future = executor.scheduleAtFixedRate(
                () -> EventQueue.invokeLater(command), initialDelay,
                period, unit);
        return () -> future.cancel(false);
    }

    public static Task scheduleWithFixedDelay(Runnable command,
            long initialDelay, long delay, TimeUnit unit) {
        ScheduledFuture<?> future = executor.scheduleAtFixedRate(
                () -> EventQueue.invokeLater(command), initialDelay, delay,
                unit);
        return () -> future.cancel(false);
    }

    public static void shutdown() {
        executor.shutdown();
    }
}

【讨论】:

  • “如果有一些名为 GuiTimer 的实用程序会很好” - 你的意思是,像 Swing Timer 这样的东西?
  • @MadProgrammer 事实证明我重新发明了轮子。很遗憾我不知道摇摆计时器,很遗憾直到现在没有人纠正我。我会更新我的答案,谢谢!
猜你喜欢
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-03
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多