Java中的巧妙异步重绘

Jea*_*ves 7 java multithreading java.util.concurrent

我有一个用例来自GUI问题,我想提交给你的睿智.

用例

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

但是计算可能需要几秒钟,而滑块调整可以每隔几百毫秒触发一次事件.

如何编写一个能够监听这些事件的正确线程,并对它们进行过滤以使结果的重绘更加生动?理想情况下,你会喜欢这样的东西

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

我试过了什么

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

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

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
    ...
}
Run Code Online (Sandbox Code Playgroud)

每当GUI发送一个事件表明参数已被更改时,我们就会调用updater.doUpdate().这导致方法refresh()被调用得更少.但我无法控制这一点.

其他方式?

我想知道是否有另一种方法可以使用jaca.concurrent类.但我无法在Executors框架中排序我应该开始的那个.

你们中有没有一些类似用例的经验?

谢谢

And*_*Mao 4

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

SwingWorker为每个请求触发一个。如果有新的请求进来,而工作人员还没有完成,你可以cancel(),然后开始一个新的SwingWorker. 关于另一张海报所说的,我不认为publish()并且process()是您正在寻找的(尽管它们也非常有用),因为它们适用于工作人员可能比 GUI 处理事件的速度更快的情况。

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
    }       
}
Run Code Online (Sandbox Code Playgroud)

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

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