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

My Swing GUI показва 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, отколкото за масив; въпреки това все още получавам изключения за индекс на 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, модифицира модела на списъка (дори такъв, който има защита на паралелността), има риск да го направи по време на преначертаване на GUI от EDT. Ако преначертаването изисква повече от един достъп до модела на списъка, тогава фалшива модификация от друга нишка вероятно ще унищожи всички предположения за стабилност, направени от рутинната процедура за преначертаване на GUI. Това е само предположение; Чудя се дали някой друг може да хвърли светлина... - person Chap; 13.08.2011