3

我的 Swing GUI 显示由后台线程按顺序删除的项目的 JList。

根据 AbstractListModel 的约定,在 JList 后面是一个ArrayDeque<Card>myHopper 实现myHopper.getSize()myHopper.getElementAt()

后台线程使用删除项目myHopper.poll()

毫不奇怪,我目前正在使 AWT 数组索引超出范围异常。

我应该怎么做才能在 EDT 线程和我的后台线程之间正确同步对 myList 的访问?我已经看到参考,Collections.synchronizedList(arrayList)但我认为这不适合我的 ArrayDeque。

4

4 回答 4

5

您是否尝试过仅使用LinkedBlockingDeque而不是 ArrayDeque?

于 2011-08-13T04:27:27.700 回答
3

对我的问题的简短回答似乎是“你不能:你绝不能尝试从 EDT 以外的任何线程访问 Swing 组件 [包括它的模型]。”

这篇文章展示了我最终是如何解决这个问题的。一个工作线程需要从 JList 的模型中提取一个项目,并且这样做是为了在 EDT 上invokeAndWait()安排该工作,然后等待该任务完成,然后继续。

使用同步的 LinkedBlockingDeque 不起作用,我怀疑这是因为 EDT在更新 GUI 组件时对 Deque 接口进行了一系列非原子调用。另一个线程在调用之间对模型的任何更改都可能破坏 EDT 所做的任何稳定性假设。

(也许这就是在整个 Swing 文档中出现的持久“警告: Swing 不是线程安全的”所暗示的。)

于 2011-08-18T04:56:14.410 回答
1

下面的代码对我很有效,可能会给你一些想法。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Timer;

public class JListDemo {
    public static void main(String[] args) {
        final MyListModel model = new MyListModel();

        // set up a background task to periodically purge items from the list
        java.util.Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                String item = model.poll();
                if (item != null) {
                    System.out.println("Removed " + item + " from list");
                } else {
                    System.out.println("Nothing to remove off list, click 'Add Item' button to add more");
                }
            }
        }, 1000, 2000);

        JList list = new JList(model);

        // Add a button to add new items to the list
        JButton button = new JButton("Add Item");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                model.offer(new Date().toString());
            }
        });

        JFrame frame = new JFrame("JList Demo");
        frame.add(list);
        frame.add(button, BorderLayout.SOUTH);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(200, 200);
        frame.setVisible(true);
    }

    private static class MyListModel extends DefaultListModel {
        private final ArrayDeque<String> dq = new ArrayDeque<String>();

        public synchronized String poll() {
            String head = dq.poll();
            if (head != null) {
                removeElementAt(0);
            }
            return head;
        }

        public synchronized void offer(String item) {
            dq.offer(item);
            insertElementAt(item, getSize());
            System.out.println("Added " + item + " to list");
        }
    }

}
于 2011-08-13T19:43:51.027 回答
0

改为使用 SwingWorker 执行您的操作。

http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html

于 2011-08-13T10:55:53.600 回答