首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Java中巧妙的异步重绘

Java中巧妙的异步重绘
EN

Stack Overflow用户
提问于 2013-03-14 14:37:41
回答 5查看 1.6K关注 0票数 7

我有一个用例来自一个GUI问题,我想提交给您的智慧。

用例

我有一个GUI,它根据用户在GUI中设置的一些参数显示计算结果。例如,当用户移动滑块时,会触发多个事件,这些事件都会触发新的计算。当用户将滑块值从A调整到B时,会触发数十个事件。

但是计算时间可以长达几秒钟,而滑块调整可以每隔100 ms触发一次事件。

如何编写一个正确的线程来侦听这些事件,并对它们进行过滤,以便重新绘制结果是生动的?理想情况下,你会喜欢这样的东西

  • 接收到第一次更改事件后,立即启动新的计算;
  • 如果接收到新事件,取消第一次计算,并使用新的参数启动新的计算;
  • 但是要确保最后一个事件不会丢失,因为最后一个完成的计算需要有最后更新的参数。

我已经尝试过的

我的一个朋友(A. Cardona)提出了一种低级别的更新线程方法,它可以防止太多事件触发计算。我在这里复制粘贴它(GPL):

他把它放在一个扩展Thread的类中:

代码语言:javascript
运行
复制
public void doUpdate() {
    if (isInterrupted())
        return;
    synchronized (this) {
        request++;
        notify();
    }
}

public void quit() {
    interrupt();
    synchronized (this) {
        notify();
    }
}

 public void run() {
    while (!isInterrupted()) {
        try {
            final long r;
            synchronized (this) {
                r = request;
            }
            // Call refreshable update from this thread
            if (r > 0)
                refresh(); // Will trigger re-computation
            synchronized (this) {
                if (r == request) {
                    request = 0; // reset
                    wait();
                }
                // else loop through to update again
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


public void refresh() {
    // Execute computation and paint it
    ...
}

每次事件由图形用户界面发送,声明参数已被更改,我们称之为updater.doUpdate()。这导致调用refresh()方法的次数要少得多。但我无法控制这件事。

换条路?

我想知道是否还有其他方法可以使用jaca.concurrent类。但是,我不能在执行者的框架中对我应该从哪一个开始进行排序。

你们中有谁对类似的用例有一些经验吗?

谢谢

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-03-14 15:39:36

如果您正在使用Swing,则SwingWorker提供了这方面的功能,您不必自己处理线程池。

为每个请求启动一个SwingWorker。如果一个新请求传入,而工作人员未完成,您可以对其进行cancel(),并启动一个新的SwingWorker。关于另一张海报上说的话,我不认为publish()process()是您要寻找的(尽管它们也非常有用),因为它们的目的是在这样的情况下,工作人员可能会比GUI更快地触发事件。

代码语言:javascript
运行
复制
ThingyWorker worker;

public void actionPerformed(ActionEvent e) {
    if( worker != null ) worker.cancel();
    worker = new ThingyWorker();
    worker.execute();
}

class ThingyWorker extends SwingWorker<YOURCLASS, Object> {
    @Override protected YOURCLASS doInBackground() throws Exception {
        return doSomeComputation(); // Should be interruptible
    }   
    @Override protected void done() {
        worker = null; // Reset the reference to worker

        YOURCLASS data;

        try {
            data = get();
        } catch (Exception e) { 
            // May be InterruptedException or ExecutionException                
            e.printStackTrace();
            return;
        }           

        // Do something with data
    }       
}

操作和done()方法都是在同一个线程上执行的,因此它们可以有效地检查对是否存在现有工作人员的引用。

请注意,这实际上是在执行允许GUI取消现有操作的相同操作,除非在触发新请求时自动完成取消操作。

票数 4
EN

Stack Overflow用户

发布于 2013-03-14 15:16:53

我将通过使用队列在GUI和控件之间提供更大程度的断开。

如果在两个进程之间使用BlockingQueue。每当控件更改时,您都可以将新设置张贴到队列中。

您的图形组件可以随时读取队列,并对到达的事件采取行动或在必要时丢弃它们。

票数 1
EN

Stack Overflow用户

发布于 2013-03-14 15:28:52

我将查看SwingWorker.publish() (http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html)

发布允许SwingWorker对象的后台线程导致对process()方法的调用,但并不是每个发布()调用都会导致进程()调用。如果在process()返回之前进行了多个进程调用,并且可以再次调用,则SwingWorker将用于将多个发布调用连接到一个进程调用中的参数连接起来。

我有一个显示正在处理的文件的进度对话框;这些文件的处理速度比UI能够跟上它们的速度要快,而且我不希望处理速度减慢以显示文件名;我使用这个对话框,只显示发送给process ()的最后文件名;在本例中,我只想向用户指示当前处理的位置,他们无论如何都不会读取所有文件名。我的UI在这方面工作得非常顺利。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15412260

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档