文档

Java™教程
隐藏目录
简单的后台任务
指南:使用Swing创建GUI
课程:Swing中的并发性
章节:工作线程和SwingWorker

简单的后台任务

让我们从一个非常简单但潜在耗时的任务开始。 TumbleItem applet加载了一组用于动画的图形文件。如果图形文件从初始线程加载,GUI可能会有延迟才会出现。如果图形文件从事件分派线程加载,GUI可能会暂时无响应。

为了避免这些问题,TumbleItem从其初始线程创建并执行一个SwingWorker实例。该对象的doInBackground方法在工作线程中执行,将图像加载到ImageIcon数组中,并返回对它的引用。然后done方法在事件分派线程中执行,调用get来检索此引用,并将其分配给名为imgs的applet类字段。这样TumbleItem可以立即构建GUI,而无需等待图像加载完成。

这是定义和执行SwingWorker对象的代码。

SwingWorker worker = new SwingWorker<ImageIcon[], Void>() {
    @Override
    public ImageIcon[] doInBackground() {
        final ImageIcon[] innerImgs = new ImageIcon[nimgs];
        for (int i = 0; i < nimgs; i++) {
            innerImgs[i] = loadImage(i+1);
        }
        return innerImgs;
    }

    @Override
    public void done() {
        //移除“正在加载图像”标签。
        animator.removeAll();
        loopslot = -1;
        try {
            imgs = get();
        } catch (InterruptedException ignore) {}
        catch (java.util.concurrent.ExecutionException e) {
            String why = null;
            Throwable cause = e.getCause();
            if (cause != null) {
                why = cause.getMessage();
            } else {
                why = e.getMessage();
            }
            System.err.println("Error retrieving file: " + why);
        }
    }
};

SwingWorker的所有具体子类都实现doInBackgrounddone的实现是可选的。

请注意,SwingWorker是一个泛型类,有两个类型参数。第一个类型参数为doInBackground指定了一个返回类型,也为get方法指定了一个返回类型,其他线程可以通过调用get方法来检索doInBackground返回的对象。SwingWorker的第二个类型参数指定了在后台任务仍然活动时返回的中间结果的类型。由于此示例不返回中间结果,因此使用Void 作为占位符。

你可能会想知道设置imgs的代码是否过于复杂。为什么要让doInBackground返回一个对象,并使用done来获取它?为什么不直接让doInBackground直接设置imgs呢?问题是imgs引用的对象是在工作线程中创建的,并在事件分发线程中使用。当对象在这种方式下在线程之间共享时,必须确保在一个线程中所做的更改对另一个线程可见。使用get可以保证这一点,因为使用get会在创建imgs的代码和使用它的代码之间创建一个happens before关系。有关happens before关系的更多信息,请参考内存一致性错误并发教程。

实际上有两种方法可以获取doInBackground返回的对象。

在从事件分发线程调用get的时候要小心;在get返回之前,没有GUI事件被处理,GUI界面会"冻结"。除非你确信后台任务已经完成或接近完成,否则不要调用没有参数的get

有关TumbleItem示例的更多信息,请参考教程如何使用Swing定时器在课程使用其他Swing功能中。


上一页:工作线程和SwingWorker
下一页:具有中间结果的任务