Как мне управлять многопоточным одновременным доступом к ArrayDeque?

Мой графический интерфейс Swing отображает JList элементов, которые последовательно удаляются фоновым потоком.

За JList стоит ArrayDeque<Card>, myHopper, реализующий myHopper.getSize() и myHopper.getElementAt() в соответствии с контрактом AbstractListModel.

Фоновый поток удаляет элементы с помощью myHopper.poll().

Неудивительно, что в настоящее время я получаю индекс массива AWT за пределами исключений.

Что я должен сделать, чтобы правильно синхронизировать доступ к myList между потоком EDT и моим фоновым потоком? Я видел ссылки на Collections.synchronizedList(arrayList), но не думаю, что это подходит для моего ArrayDeque.


person Chap    schedule 13.08.2011    source источник


Ответы (4)


Пробовали ли вы просто использовать LinkedBlockingDeque вместо ArrayDeque?

person Maurício Linhares    schedule 13.08.2011
comment
Я смог заменить его без каких-либо жалоб со стороны компилятора, и я подозреваю, что связанный список может быть лучшей реализацией Deque, чем Array; однако я все еще получаю исключения индекса массива AWT за пределами границ. Я нашел stackoverflow.com/questions/3440360/ что указывает на то, что не следует никогда изменять модель в любом потоке, кроме EDT. Отчасти это имеет смысл, хотя я не могу точно сказать, почему. Вернуться к доске для рисования.... - person Chap; 13.08.2011

Короткий ответ на мой вопрос выглядит так: «Вы не можете: вы никогда не должны пытаться получить доступ к компоненту Swing [и это включает его модель] из любого потока, кроме EDT».

Этот пост показывает, как В конце концов я решил проблему. Рабочий поток должен извлечь элемент из модели JList и делает это, используя invokeAndWait() для планирования этой работы в EDT, а затем ждет, пока эта задача не будет выполнена, а затем продолжает работу.

Использование синхронизированного LinkedBlockingDeque не сработало, и я подозреваю, что это связано с тем, что EDT выполняет неатомарную серию вызовов интерфейса Deque при обновлении компонента GUI. Любое изменение модели между вызовами другим потоком может разрушить любые предположения о стабильности, которые делает EDT.

(Возможно, именно на это намекает постоянное «Предупреждение: Swing не является потокобезопасным», которое появляется в документации Swing.)

person Chap    schedule 18.08.2011

Следующий код хорошо работает для меня и может дать вам некоторые идеи.

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");
        }
    }

}
person Binil Thomas    schedule 13.08.2011

Вместо этого выполняйте свои операции с помощью SwingWorker.

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

person asahin    schedule 13.08.2011
comment
Могу ошибаться, но не думаю, что это поможет. Насколько я понимаю, если любой поток, кроме EDT, изменяет модель списка (даже тот, у которого есть защита от параллелизма), он рискует сделать это во время перерисовки графического интерфейса с помощью EDT. Если для перерисовки требуется более одного доступа к модели списка, то мошенническая модификация другим потоком, вероятно, разрушит любые предположения о стабильности, сделанные процедурой перерисовки GUI. Это всего лишь предположение; Интересно, может ли кто-нибудь еще пролить свет... - person Chap; 13.08.2011