Updating DefaultListModel in another thread

I have a program that should update the contents of a JList that has a DefaultListModel in another thread. Since the amount of content may change from time to time, I just clear all the content and add new content to the DefaultListModel when updating. But it looks like I ran into a problem when the JFrame starts updating while my thread is doing the update. I have exceptions like this

 Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 3 

Here is a sample code

  DefaultListModel model; JList jList; JScrollPane jScrollPane; Thread thread; public Frame() { this.setTitle("ASM_SIMULATOR"); this.setBounds(100, 100, 500, 500); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.getContentPane().setLayout(null); model = new DefaultListModel(); jList = new JList(model); jScrollPane = new JScrollPane(jList); jList.setBounds(50, 50, 300, 200); jScrollPane.setBounds(50, 50, 300, 200); this.getContentPane().add(jScrollPane); this.setVisible(true); thread = new Thread(new Runnable() { @Override public void run() { while (true) { makeData(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }); thread.start(); } public void makeData() { System.out.println("makeData()"); model.clear(); for (int i = 0; i < 20; i++) { model.addElement((int) (Math.random() * 100)); } } public static void main(String[] args) { new Frame(); } 
+4
source share
4 answers

You break the basic β€œall Swing component should be available / changed in Thread Dispatch Thread (= EDT), and on EDTβ€œ only twice ”in this code fragment.

  • Your main method should wrap the new Frame() call in SwingUtilities#invokeLater or some similar method
  • Your model update thread changes the model in the background thread. Updating the model will crash the events that were received using the JList , where the JList updates itself (again, in the wrong thread).

Two possible solutions:

  • create a new DefaultListModel in the background thread and replace it at a time with EDT.
  • keep updating the existing model, but make sure the updates happen in the EDT.
+3
source

The main answer:

Swing is not thread safe.

What you need to do is either use SwingWorker to build the model and use its done / process method to apply it to the view, or use SwingUtilities.invokeLater to continue using your stream, but synchronize is updated back to the Dispatcher event stream

Read Concurrency in Swing for more details.

+3
source
  • you have a problem with concurrency in swing

  • need to wrap model.addElement((int) (Math.random() * 100)); in invokeLater

  • The correct way can be started by Thread workers from Runnable#Thread or use SwingWorker

  • output from SwingWorker publish() and process() methods can be set to EDT

+2
source

Unfortunately, this is not so simple. Only the GUI thread should be allowed to update the GUI, so any other thread should redirect any updates to the GUI thread through SwingUtilities.InvokeLater . In your case, you can simply wrap the entire makeData method, since all it does is update the GUI:

  thread = new Thread(new Runnable() { @Override public void run() { while (true) { SwingUtilities.InvokeLater(new Runnable() { public void run() { makeData(); } }); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }); 

Note that now the makeData code will execute in the GUI thread. In other cases, when you are doing other laborious work that is not related to the GUI, you should use InvokeLater more subtle way to keep the user interface flow as free as possible.

Edit : By looking more closely at my code, I noticed that all you do is synchronize the GUI update every 200 ms. You can make it a lot easier with javax.swing.Timer :

 int delay = 200; //milliseconds ActionListener taskPerformer = new ActionListener() { public void actionPerformed(ActionEvent evt) { makeData(); } }; new Timer(delay, taskPerformer).start(); 
0
source

Source: https://habr.com/ru/post/1442446/


All Articles