Clever asynchronous repaint in Java

你说的曾经没有我的故事 提交于 2019-12-05 05:12:53

If you're using Swing, the SwingWorker provides capabilities for this, and you don't have to deal with the thread pool yourself.

Fire off a SwingWorker for each request. If a new request comes in and the worker is not done, you can cancel() it, and just start a new SwingWorker. Regarding what the other poster said, I don't think publish() and process() are what you are looking for (although they are also very useful), since they are meant for a case where the worker might fire off events faster than the GUI can process it.

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
    }       
}

Both the action and the done() method are executed on the same thread, so they can effectively check the reference to whether there is an existing worker.

Note that effectively this is doing the same thing that allows a GUI to cancel an existing operation, except the cancel is done automatically when a new request is fired.

I would provide a further degree of disconnect between the GUI and the controls by using a queue.

If you use a BlockingQueue between the two processes. Whenever the controls change you can post the new settings to the queue.

Your graphics component can read the queue whenever it likes and act on the arriving events or discard them as necessary.

I would look into SwingWorker.publish() (http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html)

Publish allows the background thread of a SwingWorker object to cause calls to the process() method, but not every publish() call results in a process() call. If multiple process calls are made before process() returns and can be called again, SwingWorker concatenates the parameters used for multiple publish calls into one call to process.

I had a progress dialog which displayed files being processed; the files were processed faster than the UI could keep up with them, and I didn't want the processing to slow down to display the file names; I used this and had process display only the final filename sent to process(); all I wanted in this case was to indicate to the user where the current processing was, they weren't going to read all the filenames anyway. My UI worked very smoothly with this.

Take a look at the implementation of javax.swing.SwingWorker (source code in the Java JDK), with a focus on the handshaking between two methods: publish and process.

These won't be directly applicable, as-is, to your problem - however they demonstrate how you might queue (publish) updates to a worker thread and then service them in your worker thread (process).

Since you only need the last work request, you don't even need a queue for your situation: keep only the last work request. Sample that "last request" over some small period (1 second), to avoid stopping/restarting many many times every 1 second, and if it's changed THEN stop the work and restart.


The reason you don't want to use publish / process as-is is that process always runs on the Swing Event Dispatch Thread - not at all suitable for long running calculations.

The key here is that you want to be able to cancel an ongoing computation. The computation must frequently check a condition to see if it needs to abort.

volatile Param newParam;

Result compute(Param param)
{
    loop
        compute a small sub problem
        if(newParam!=null) // abort
            return null;  

    return result
}

To handover param from event thread to compute thread

synchronized void put(Param param)  // invoked by event thread
    newParam = param;
    notify();

synchronized Param take()
    while(newParam==null)
        wait();
    Param param = newParam;
    newParam=null;
    return param;

And the compute thread does

public void run()
    while(true)
        Param param = take();
        Result result = compute(param);
        if(result!=null)
            paint result in event thread
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!