为什么java swing不允许来自ExecutorService Thread

时间:2015-05-30 13:29:08

标签: java multithreading swing user-interface

我的java GUI管理存在问题。我正在制作更新程序,但是当我尝试从ExecutorService线程编辑某些JText或JProgressbar时,没有...

为什么它坏了?这是我的ControllerManager:

public class UpdateManager extends AppManager {
    private final List<Controller> controllers;
    @Getter private final ExecutorService worker;
    @Getter private final UserInterface form;
    private boolean isEnd;

    public UpdateManager() {
        this.controllers = new ArrayList<>();
        this.form = new Form(this);
        this.worker = Executors.newCachedThreadPool();
    }

    @Override
    public void start(Controller... controllers) {
        form.initialize();

        this.controllers.addAll(Arrays.asList(controllers));

        for(Controller controller: controllers)
            controller.start();
    }

    @Override
    public void end(boolean dispose) {
        if(isEnd) return;

        for(Controller controller: controllers)
            controller.end();

        if(dispose) form.dispose();
        isEnd = true;
    }
}

以下是我尝试更改某些标签的示例:

/**
 * Created by romain on 17/05/2015.
 */
public class ReleaseController implements Controller {
    private final AppManager manager;
    @Getter private final LinkedBlockingDeque<URL> files;
    private Future<?> future;
    private final SerializedObject<SerializedReleases> serializedReleases;
    private final SerializedObject<Integer> serializedRelease, serializedTimestamp;

    public ReleaseController(AppManager manager) {
        this.manager = manager;
        this.files = new LinkedBlockingDeque<>();
        this.serializedReleases = SerializedObjectImpl.create(FileUtils.path("releases", "releases.dat"), true, null);
        this.serializedRelease = SerializedObjectImpl.create(FileUtils.path("swtour", "release.int"), false, 0);
        this.serializedTimestamp = SerializedObjectImpl.create(FileUtils.path("swtour", "timestamp.int"), false, 0);
    }

    /**
     * TODO: checking local files
     */
    @Override
    public void start() {
        this.future = manager.getWorker().submit(new Runnable() {
            @Override
            public void run() {
                int release = serializedRelease.get();
                int serverRelease = serializedReleases.get().lastRelease(AppUtils.OS);
                int result = serverRelease - release;

                if(result == 0 || result < 0) {
                    manager.getForm().alreadyUpdated(); //HERE
                    return;
                }

                for(int i=release+1;i<serverRelease;i++) {
                    try {
                        files.addLast(new URL(
                                FileUtils.path(Main.SERVER, "releases", AppUtils.OS.toString(), i + ".zip")));
                    } catch(Exception e) {
                        System.out.println(e.getMessage());
                    }
                }

                serializedRelease.setObject(serverRelease).write();
                serializedTimestamp.setObject((int)System.currentTimeMillis()).write();
            }
        });
    }

    @Override
    public void end() {
        if(future != null && !future.isCancelled())
            future.cancel(true);
    }

}

如果你看得对,那就:

manager.getForm().alreadyUpdated();

修改我的GUI:

public void alreadyUpdated() {
        content.getFirstLine().setText("");
        content.getSecondLine().setText("Your client is already up-to-date!");
        content.getPlayButton().setEnabled(true);
}

但没什么..我的gui没变! 谢谢

1 个答案:

答案 0 :(得分:3)

尝试从另一个线程更新Swing数据结构时有两个问题:

  1. Swing并非设计为多线程安全。你可能会破坏它的数据结构。
  2. 没有任何东西迫使它看到在另一个线程中做出的更改。它可以在不受内存更改影响的寄存器或高速缓存中运行。
  3. 正如评论中已经提到的,您需要使用invokeAndWaitinvokeLater在Swing事件处理线程中进行更改。