How to show JList from a blockingQueue?

62 Views Asked by At

I'm using multi threading with Java, I have a thread that will post a message into a queue, a blockingQueue which is thread safe, and I have another thread, implementing a GUI with swing. Everytime I'm checking whether the queue is empty or not, if not, I poll the message and add it to DefaultListModel, but the problem the display is not updated. I made sure that the message polled is not empty. This is the code: For the implementation of the JList

historyListModel = new DefaultListModel<String>();
        historyList = new JList<String>(historyListModel);
        historyList.setAutoscrolls(true);



        JScrollPane historyScroll = new JScrollPane(historyList);
        add(historyScroll, BorderLayout.CENTER);

Adding message to the queue

private BlockingQueue<String> messageRes = new LinkedBlockingQueue<>();

DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                        
socket.receive(packet);
String msgReceived = new String(packet.getData(), 0, packet.getLength());
                        
messageRes.add(msgReceived);

The thread to update the display

private class update implements Runnable{

        @Override
        public void run() {
            while (true){
                
                if (!messageRes.isEmpty()){
                    historyListModel.addElement(messageRes.poll());
                    

                }
                
            }

        }
    }

Then this thread is called in the GUI main window

new Thread(new update()).start();

In order to make some tests, I tried the following code and It worked


private class update implements Runnable{

        @Override
        public void run() {
            while (true){
                
                Thread.sleep(1000)
                historyListModel.addElement("hello world);
                    

                
                
            }

        }
    }

The previous code allows me to update the display every 1 second. I tried one more code in order to triangulate the error:

private class update implements Runnable{

        @Override
        public void run() {
            while (true){
                
                if (!messageRes.isEmpty()){
                    historyListModel.addElement(messageRes.poll());
                    historyListModel.addElement("hello world);
                    

                }
                
            }

        }
    }

No display was updated with the previous code. Could anyone propose any explanation to what is happening ?

Thank you.

1

There are 1 best solutions below

1
MadProgrammer On

Swing is not thread-safe and is single-threaded. This means you should not be updating the UI from outside of the context of the Event Dispatching Thread nor should you be executing long-running/blocking calls within its context.

In your case, a SwingWorker would probably be the best solution, as you can poll the queue for new messages and publish them to the UI in a safe manner.

See...

for more details.

One thing I did note during my testing was this...

@Override
public void run() {
    while (true){
        if (!messageRes.isEmpty()){
            historyListModel.addElement(messageRes.poll());
        }
    }
}

was probably causing the UI to be overloaded, as poll will either return the next element or null if there are none. So it was a "wild loop". Instead, you should probably be using take which will wait till a new value is available.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JList<String> messageList;
        private DefaultListModel<String> model;

        public TestPane() {
            model = new DefaultListModel<>();
            messageList = new JList<>(model);
            setLayout(new BorderLayout());
            add(new JScrollPane(messageList));

            Consumer consumer = new Consumer();
            Producer producer = new Producer();

            ConsumerWoker worker = new ConsumerWoker(consumer, model);
            worker.execute();

            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        while (true) {
                            consumer.add(producer.next());
                            Thread.sleep(500);
                        }
                    } catch (InterruptedException ex) {
                    }
                }
            });
            thread.start();
        }

    }

    public class Producer {

        private List<String> master = Arrays.asList(new String[]{
            "Malachy Keller",
            "Ruqayyah Galvan",
            "Harris Nunez",
            "Nojus Riggs",
            "Joan Mercer",
            "Lynda Solomon",
            "Raiden Fitzpatrick",
            "Sebastian Ahmed",
            "Jo Short",
            "Nabeel Howarth",
            "Maira Garrett",
            "Patrik Knights",
            "Mimi Mcgill",
            "Antonina Villanueva",
            "Kenya Hyde",
            "Aleksander Rigby",
            "Hasan Gilmore",
            "Jessica Mcculloch",
            "Seth Black",
            "Marjorie Brewer",
            "Elliot Gay",
            "Oluwatobiloba Bowman",
            "Domonic Saunders",
            "Braden Hale",
            "Muneeb Rankin",
            "Ruby Tapia",
            "Iris Hines",
            "Afsana Ponce",
            "Beverly Soto",
            "Presley Bloggs",
            "Leopold Goddard",
            "Missy Browne",
            "Deniz Woodcock",
            "Gwion Ferreira",
            "Stanley Mccall",
            "Jayda Christie",
            "Nikhil Plummer",
            "Stacy Crosby",
            "Cally Henry",
            "Lilliana Taylor",
            "Dolcie Navarro",
            "Merryn Reynolds",
            "Annalise Boyce",
            "Anaya Cisneros",
            "Aimie Piper",
            "Celine Pearson",
            "Clayton Battle",
            "Danielle Briggs",
            "Maddison Couch",
            "Jorden Keeling",
            "Iylah Holmes",
            "Bethaney Quintero",
            "Dominique Brett",
            "Rohit Benjamin",
            "Edgar Rodgers",
            "Petra Salgado",
            "Myrtle Deleon",
            "Letitia Sheridan",
            "Wasim Chester",
            "Leela Simpson",
            "Aine Rojas",
            "Ava Mclean",
            "Jerry Caldwell",
            "Fraser Prosser",
            "Callum Vang",
            "Yasmin Ochoa",
            "Gaia Daly",
            "Vanessa Mathews",
            "Scarlett Brook",
            "Rhiann Fox",
            "Nansi Cote",
            "Dwayne Rowley",
            "Junior Lucas",
            "Becky Rush",
            "Lori Guthrie",
            "Safa Reed",
            "Merlin Cartwright",
            "Misbah Trejo",
            "Khaleesi Ellison",
            "Lena Wood",
            "Bluebell Coffey",
            "Sherry Hutton",
            "Abi Delacruz",
            "Kwabena Bright",
            "Anastazja Kumar",
            "Bronwyn Huffman",
            "Atif Burke",
            "Arwen Kirby",
            "Bobbie Noble",
            "Blane Bauer",
            "Zander Sparrow",
            "Marius Wormald",
            "Rajan Perez",
            "Teejay Faulkner",
            "Imaani Rodriquez",
            "Safaa Middleton",
            "Rafael Livingston",
            "Oakley Swan",
            "Samiya Kim",
            "Glen Beasley"
        });

        private List<String> avaliableMessages;

        public Producer() {
            avaliableMessages = new ArrayList<>(100);
        }

        public String next() {
            if (avaliableMessages.isEmpty()) {
                avaliableMessages.addAll(master);
            }
            return avaliableMessages.remove(0);
        }
    }

    public class Consumer {

        private BlockingQueue<String> messages = new LinkedBlockingDeque<>();

        public void add(String message) {
            messages.add(message);
        }

        public String next() throws InterruptedException {
            return messages.take();
        }
    }

    public class ConsumerWoker extends SwingWorker<Void, String> {

        private AtomicBoolean keepRunning = new AtomicBoolean(true);

        private Consumer consumer;
        private DefaultListModel model;

        public ConsumerWoker(Consumer consumer, DefaultListModel model) {
            this.consumer = consumer;
            this.model = model;
        }

        public void stop() {
            keepRunning.set(false);
        }

        public Consumer getConsumer() {
            return consumer;
        }

        @Override
        protected Void doInBackground() throws Exception {
            while (keepRunning.get()) {
                String message = getConsumer().next();
                publish(message);
            }
            return null;
        }

        @Override
        protected void process(List<String> chunks) {
            for (String msg : chunks) {
                model.addElement(msg);
            }
        }

    }
}