Почему возникает исключение IndexOutOfBoundsException в AbstractListModel

465
13 января 2017, 08:14

Исключение выходит если запускаю в потоке, а без потока не появляется вроде бы. Как подправить чтоб без бага было.

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
at java.util.LinkedList.checkElementIndex(Unknown Source)
at java.util.LinkedList.get(Unknown Source)
at ta.MyListModel.getElementAt(MyListModel.java:31)
at ta.MyListModel.getElementAt(MyListModel.java:1)
at javax.swing.plaf.basic.BasicListUI.updateLayoutState(Unknown Source)
at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(Unknown Source)
at javax.swing.plaf.basic.BasicListUI.getPreferredSize(Unknown Source)
at javax.swing.JComponent.getPreferredSize(Unknown Source)
at javax.swing.ScrollPaneLayout.layoutContainer(Unknown Source)
at java.awt.Container.layout(Unknown Source)
at java.awt.Container.doLayout(Unknown Source)
at java.awt.Container.validateTree(Unknown Source)
at java.awt.Container.validate(Unknown Source)
at javax.swing.RepaintManager$3.run(Unknown Source)
at javax.swing.RepaintManager$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at javax.swing.RepaintManager.validateInvalidComponents(Unknown Source)
at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Класс модели

package ta;
import java.math.BigDecimal;
import java.util.LinkedList;
import javax.swing.AbstractListModel;
public class MyListModel extends AbstractListModel<String>{
private static final long serialVersionUID = 1L;
private LinkedList<BigDecimal> listBets;
public LinkedList<BigDecimal> getListBets() {
    return listBets;
}
public void setListBets(LinkedList<BigDecimal> listBets) {
    this.listBets = listBets;
}
public MyListModel(LinkedList<BigDecimal> list) {
    setList(list);
}
public void add(BigDecimal value){
    getList().add(value);
    refresh();      
}
public String getElementAt(int index) {
    return String.format("%.8f", getList().get(index));
}
public int getSize() {
    return getList().size();
}
public LinkedList<BigDecimal> getList(){
    return listBets;
}
public void setList(LinkedList<BigDecimal> list){
    this.listBets = list;
    refresh();
}
public void clear() {
    getList().clear();
    refresh();
}
public void removeSide() {
        getList().removeLast();
        getList().removeFirst();
        refresh();
}
void removeLast(){
    getList().removeLast();
    refresh();
}
public void refresh() {
        fireContentsChanged(this, 0, getSize());
}
}

Класс с методом main

package ta;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Scanner;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.text.ParseException;
import java.awt.event.ActionEvent;
public class TA {
private JFrame frame;
private MyListModel listModelBets;
private JList<String> list;
private JTextArea textArea;
private final String defaultBets = "0.00000001\n0.00000001\n0.00000001\n0.00000001\n0.00000001";
private final boolean win = true;
private final boolean lose = false;
/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                TA window = new TA();
                window.frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}
/**
 * Create the application.
 */
public TA() {
    listModelBets = new MyListModel(new LinkedList<>());
    initialize();
}
/**
 * Initialize the contents of the frame.
 */
private void initialize() {
    frame = new JFrame();
    frame.setBounds(100, 100, 450, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().setLayout(new BorderLayout(0, 0));
    JPanel panel_2 = new JPanel();
    frame.getContentPane().add(panel_2);
    panel_2.setLayout(new GridLayout(0, 2, 0, 0));
    JPanel panel_1 = new JPanel();
    panel_2.add(panel_1);
    panel_1.setLayout(new BorderLayout(0, 0));
    JScrollPane scrollPane = new JScrollPane();
    panel_1.add(scrollPane);
    textArea = new JTextArea(defaultBets);
    scrollPane.setViewportView(textArea);
    JPanel panel = new JPanel();
    panel_2.add(panel);
    panel.setLayout(new BorderLayout(0, 0));
    JScrollPane scrollPane_1 = new JScrollPane();
    panel.add(scrollPane_1);
    list = new JList<String>(listModelBets);
    scrollPane_1.setViewportView(list);
    JPanel panel_3 = new JPanel();
    frame.getContentPane().add(panel_3, BorderLayout.SOUTH);
    JButton btnNewButton_1 = new JButton("Clear");
    btnNewButton_1.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            listModelBets.clear();
        }
    });
    panel_3.add(btnNewButton_1);
    JButton btnPlay = new JButton("Play");
    btnPlay.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            Thread th = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        boolean result = play();
                        if (result) {
                            if (listModelBets.getSize() == 1) {
                                listModelBets.removeLast();
                            } else if (listModelBets.getSize() > 1) {
                                listModelBets.removeSide();
                            }
                        } else {
                            listModelBets.add(getBet());
                        }
                    }
                }
            });
            th.start();
        }
    });
    panel_3.add(btnPlay);
}
void addBets() {
    Scanner sc = new Scanner(textArea.getText());
    sc.useDelimiter("\\n");
    while (sc.hasNext()) {
        String s = sc.next();
        if (s != null && !s.equals("")) {
            try {
                BigDecimal b = new BigDecimal(s);
                listModelBets.add(b);
            } catch (NumberFormatException ex) {
                // JOptionPane.showMessageDialog(frame, "Enter number
                // value.\nSample 0.00000001");
            }
        }
    }
    sc.close();
}
boolean play() {
    Random rnd = new Random();
    return rnd.nextBoolean();
}
BigDecimal getBet() {
    BigDecimal sum = null;
    if (listModelBets.getSize() > 0) {
        sum = calcSum();
    } else {
        addBets();
        sum = calcSum();
    }
    return sum;
}
BigDecimal calcSum() {
    BigDecimal sum = null;
    try {
        BigDecimal first = listModelBets.getList().getFirst();
        BigDecimal last = listModelBets.getList().getLast();
        sum = first.add(last);
    } catch (NoSuchElementException ex) {
        // stopGame();
        JOptionPane.showMessageDialog(frame, "Enter number value\nin editor bets.\nSample 0.00000001");
    }
    return sum;
}
}
Answer 1

Проблема ваша заключается в том, что модель MyListModel, не является потоко-безопасной. Но вы работает с ней при этом из разных потоков.

В момент получения размера списка и получения элементов, другой поток успевает удалить эти самые элементы. Поэтому и возникает ошибка.

Вижу несколько решений данной проблемы:

  • сделать модель thread-safe, самые простой способ это сделать, заменить LinkedList на альтернативу из пакета java.util.concurrent либо объявить методы synchronized.

  • Другое решение, изменять модель в одном потоке. Т.к. получения элементов происходит из потока UI, то и удаление следует сделать из него же. Для этого требуется операции удаления делать следующим образом:

    for (int i = 0; i < 1000; i++) {
       boolean result = play();
       try {
             SwingUtilities.invokeAndWait(() -> {
                     if (result) {
                          if (listModelBets.getSize() == 1) {
                              listModelBets.removeLast();
                          } else if (listModelBets.getSize() > 1) {
                              listModelBets.removeSide();
                          }
                     } else {
                          listModelBets.add(getBet());
                     }
              });
       } catch (Exception e) {
             e.printStackTrace();
       }
    }
READ ALSO
Спортивное программирование

Спортивное программирование

Прошу посоветовать книги по олимпиадному (школьному) программированию и вообще, что стоит учить, на данный момент читаю книгу Роберта Седжевика...

306
Закрепить блок меню

Закрепить блок меню

Есть такой код

293
Позиционирование flexbox

Позиционирование flexbox

День добрый! Есть такая html конструкция

379
Перенос строки textarea в ipad(safari)

Перенос строки textarea в ipad(safari)

Не работает перенос строки в textarea в ipad(safari), по-разному пробовал

341