Question

Edit: Note, this question is related to my other question here: SwingWorker - Passing parameters and returning an ArrayList<Object>

After digging around SwingWorker and in order to try to solve that question I came up with this SSCCE code.

End of Note

End of edit

Given the following SSCCE code:

import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class MainFrame extends JFrame {

    private JLabel countLabel1 = new JLabel("0");
    private JLabel statusLabel1 = new JLabel("Task not completed.");
    private JLabel statusLabel2 = new JLabel("Task not completed.");
    private JLabel statusLabel3 = new JLabel("Task not completed.");
    private JButton startButton = new JButton("Start");
    private ArrayList<Train> trainList = new ArrayList<>();
    private ArrayList<Train> processedTrains = new ArrayList<>();

    public MainFrame(String title) {
        super(title);

        setLayout(new GridBagLayout());

        countLabel1.setFont(new Font("serif", Font.BOLD, 28));

        GridBagConstraints gc = new GridBagConstraints();

        gc.fill = GridBagConstraints.NONE;

        gc.gridx = 0;
        gc.gridy = 0;
        gc.weightx = 1;
        gc.weighty = 1;
        add(countLabel1, gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.weightx = 1;
        gc.weighty = 1;
        add(statusLabel1, gc);

        gc.gridx = 0;
        gc.gridy = 2;
        gc.weightx = 1;
        gc.weighty = 1;
        add(statusLabel2, gc);

        gc.gridx = 0;
        gc.gridy = 3;
        gc.weightx = 1;
        gc.weighty = 1;
        add(statusLabel3, gc);

        gc.gridx = 0;
        gc.gridy = 4;
        gc.weightx = 1;
        gc.weighty = 1;
        add(startButton, gc);

        Train t1 = new Train();
        t1.setId(1);
        t1.setName("John");

        Train t2 = new Train();
        t2.setId(2);
        t2.setName("Mark");

        Train t3 = new Train();
        t3.setId(3);
        t3.setName("Lewis");


        trainList.add(t1);
        trainList.add(t2);
        trainList.add(t3);


        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                TrainMethods tm = new TrainMethods();

                // How can the swing worker return the array from the
                // processTrains method to this processedTrains array
                processedTrains = tm.processTrains(trainList);

                for (int i = 0; i < processedTrains.size(); i++) {
                    System.out.println("Train id: " + processedTrains.get(i).getId()
                            + " Train Name: " + processedTrains.get(i).getName());
                }
            }
        });

        setSize(200, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

    public static void main(String[] args) {
// Notice that it kicks it off on the event-dispatching thread, not the main thread.
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MainFrame mf = new MainFrame("Yo");
            }
        });
    }

    private class Train {
        String name;
        int id;

        public void setName(String name) {
            this.name = name;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public int getId() {
            return id;
        }
    }

    private class TrainMethods {

        public ArrayList processTrains(final ArrayList<Train> trainList) {
            // Add here a SwingWorker to create a new thread
            // and execute the following code and update later on
            // a progress bar
            SwingWorker<ArrayList, Integer> worker = new SwingWorker<ArrayList, Integer>() {
                @Override
                protected ArrayList doInBackground() throws Exception {
                    for(int i = 0; i<trainList.size(); i++)
                    {
                        trainList.get(i).setName("No name");
                        publish(i);
                    }
                return trainList;
                }

                @Override
                protected void process(List<Integer> chunks) {
                    int mostRecentValue = chunks.get(chunks.size() - 1);
                    countLabel1.setText(Integer.toString(mostRecentValue));
                }

                @Override
                protected void done() {
                    ArrayList<Train> status;
                    try {
                        // Retrieve the return value of doInBackground.
                        status = get();
                        statusLabel1.setText("Completed Train 1 with name: " + status.get(0).getName());
                        statusLabel2.setText("\nCompleted Train 2 with name: " + status.get(1).getName());
                        statusLabel3.setText("\nCompleted Train 3 with name: " + status.get(2).getName());
                    } catch (InterruptedException | ExecutionException ex) {
                        Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            };
            worker.execute();

            return trainList;

//            for (int i = 0; i < trainList.size(); i++) {
//                trainList.get(i).setName("No name");
//            }
//            return trainList;
        }
    }
}

Why the System.Out.Println() prints:

Train id: 1 Train Name: John
Train id: 2 Train Name: Mark
Train id: 3 Train Name: Lewis

While the user interface prints: No Name? Personally I was expecting to be printed No Nameon both.

Question 1: Why different outputs?

Question 2: How can I make it output No Name on both places (Sysout and GUI)?

_________________________ EDIT _______________________________

Question 3: Why on the second time I press the start button it will print No Nameon both?

Was it helpful?

Solution

doInBackground() runs, unsurprisingly, in the background. That is, it is executed in another thread. So you can not rely in the order of execution of that code, and your printing code. So the contents of trainsList can be modified before, after, or during the printing. You'd need to add some synchronization if you really need the results immediately (Usually you don't. That's the whole point in running the task in another thread to avoid waiting for it to be completed).

Most likely the correct solution is not have the printing code there at all, and let the SwingWorker do it's work the way it's intended. The results are available in a thread safe way in done() (and in process() for anything you need the quickly after the sub task is ready).

In the second run you get "No name", because you never reset the names to anything else, so they are what they have been set to in the previous run.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top