简单的后台任务

让我们从一个非常简单但可能很耗时的任务开始。 TumbleItemServlets 加载动画中使用的一组图形文件。如果图形文件是从初始线程加载的,则可能会延迟 GUI 出现。如果从事件分发线程加载了图形文件,则 GUI 可能暂时无响应。

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

这是定义和执行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() {
        //Remove the "Loading images" label.
        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是具有两个类型参数的泛型类。第一个 type 参数为doInBackground指定返回类型,也为get方法指定返回类型,其他线程调用该方法以检索doInBackground返回的对象。 SwingWorker的第二个类型参数为后台任务仍处于活动状态时返回的中期结果指定类型。由于此示例不返回中期结果,因此Void用作占位符。

您可能想知道设置imgs的代码是否不必要地复杂。为什么要使doInBackground返回一个对象并使用done来检索它?为什么不直接将doInBackground设置为imgs?问题在于,对象imgs是在工作线程中创建的,并在事件分发线程中使用。以这种方式在线程之间共享对象时,必须确保一个线程中所做的更改对另一线程可见。使用get可以保证这一点,因为使用get会在创建imgs的代码与使用该代码的代码之间构建关联。有关关系发生之前的更多信息,请参考Concurrency类中的内存一致性错误

实际上,有两种方法可以检索doInBackground返回的对象。

从事件分配线程调用get的重载时要小心;直到get返回,才处理任何 GUI 事件,并且 GUI 被“冻结”。除非您确信后台任务已完成或接近完成,否则不要在没有参数的情况下调用get

有关TumbleItem示例的更多信息,请参考类使用其他 Swing 功能中的如何使用摇摆计时器