Marllon Nasser
Marllon Nasser

Reputation: 390

Waiting for a task inside a thread pool using SwingWorker java

I'm simulating multiple Tasks inside a thread - when all of them should report to a GUI. So I have a unique form that has 4 Panels inside which should reply and do different Tasks.. they should each keep pooling a database and reporting stuff to the GUI. In this example, I just made it to write numbers to a textArea.

public class FormMain extends JFrame {
    private JButton btnStart;
    private JPanel _panelTop, _panelMid, _panelBot;

    private PanelFoo panelFooA, panelFooB, panelFooC, panelFooD;

    private final List<String> ugsRj = Arrays.asList("AB");
    private final List<String> ugsMg = Arrays.asList("CD", "EF");
    private final List<String> ugsBA = Arrays.asList("GH", "IJ", "KL");
    private final List<String> ugsPE = Arrays.asList("MN", "OP", "RS", "TU");

    private void initialize() {
        this._panelTop = new JPanel();
        this._panelMid = new JPanel();
        this._panelBot = new JPanel();

        this.btnStart = new JButton("Start");
        this._panelBot.add(this.btnStart);

        this.panelFooA = new PanelFoo(this.ugsRj);
        this.panelFooB = new PanelFoo(this.ugsMg);
        this.panelFooC = new PanelFoo(this.ugsBA);
        this.panelFooD = new PanelFoo(this.ugsPE);
        _panelMid.setLayout(new BoxLayout(_panelMid, BoxLayout.X_AXIS));

        this._panelMid.add(this.panelFooA);
        this._panelMid.add(this.panelFooB);
        this._panelMid.add(this.panelFooC);
        this._panelMid.add(this.panelFooD);

    }

    public FormMain() {
        initialize();
        getContentPane().setLayout(new BorderLayout());
        setSize(800, 516);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().add(this._panelTop, BorderLayout.NORTH);
        getContentPane().add(this._panelMid, BorderLayout.CENTER);
        getContentPane().add(this._panelBot, BorderLayout.SOUTH);

        this.btnStart.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                final AuthenticationUser auth = new AuthenticationUser();
                auth.setUser("test");
                auth.setPassword("p@ss");
//
                final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
                        w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
                        final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
                        w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
                        final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
                        w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
                        final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
                        w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

                        w1.execute();
                        w2.execute();
                        w3.execute();
                        w4.execute();

                        return null;
                    }

                };
                worker.execute();
            }
        });

    }
}

public class ProgressListener implements PropertyChangeListener {
    private JProgressBar bar;

    ProgressListener() {
    }

    ProgressListener(final JProgressBar b) {
        this.bar = b;
        this.bar.setValue(0);
    }

    @Override
    public void propertyChange(final PropertyChangeEvent evt) {
        // Determine whether the property is progress type
        if ("progress".equals(evt.getPropertyName())) {
            this.bar.setValue((int) evt.getNewValue());
        }
    }
}

public class PanelFoo extends JPanel {

    /**
     * 
     */
    private static final long serialVersionUID = -1400188281877395934L;
    private JLabel label;
    private JTextArea textArea;
    private JScrollPane scrollPanel;
    private JProgressBar progressBar;

    public PanelFoo(final List<String> listOfStates) {
        setLayout(new FlowLayout());
        setSize(180, 400);
        final ImageIcon icon = createImageIcon("/images/waiting-list.png", "waiting start");
        this.label = new JLabel(listOfStates.get(0), icon, SwingConstants.HORIZONTAL);
        add(this.label);

        this.textArea = new JTextArea("Numbers: \n");
        this.textArea.setWrapStyleWord(true);
        this.scrollPanel = new JScrollPane(this.textArea);
        this.scrollPanel.setPreferredSize(new Dimension(150, 350));

        this.progressBar = new JProgressBar(0, 100);

        add(this.scrollPanel);
        add(this.progressBar);

        setVisible(true);
    }

    /** Returns an ImageIcon, or null if the path was invalid. */
    public ImageIcon createImageIcon(final String path, final String description) {
        if (path != null) {
            ImageIcon imageIcon = new ImageIcon(getClass().getResource(path));
            final Image image = imageIcon.getImage(); 
            final Image newimg = image.getScaledInstance(30, 30, Image.SCALE_SMOOTH);
            imageIcon = new ImageIcon(newimg, description);
            return imageIcon;

        } else {
            System.err.println("Couldn't find file: " + path);
            return null;
        }
    }

    public final JLabel getLabel() {
        return this.label;
    }

    public final void setLabel(final JLabel label) {
        this.label = label;
    }

    public final JTextArea getTextArea() {
        return this.textArea;
    }

    public final void setTextArea(final JTextArea textArea) {
        this.textArea = textArea;
    }

    public final JProgressBar getProgressBar() {
        return this.progressBar;
    }

    public final void setProgressBar(final JProgressBar progressBar) {
        this.progressBar = progressBar;
    }

}

public class WorkerDoSomething extends SwingWorker<Void, Void> {

    private JTextArea txtArea;
    private JLabel label;
    private Random r = new Random();

    WorkerDoSomething() {
    }

    public WorkerDoSomething(final PanelFoo panelFooInstance) {
        this.txtArea = panelFooInstance.getTextArea();
        this.label = panelFooInstance.getLabel();
    }

    private Integer randomInt(final int min, final int max) {
        final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;

        return randomNumber;
    }

    @Override
    protected Void doInBackground() throws Exception {
        final Integer randomNumber = randomInt(10000000, 1000000000);
        long j;
        int progress = 0;
        final int onePerCent = randomNumber / 100;
        final int onePerMillion = onePerCent / 10;
        for (j = 0; j <= randomNumber; j++) {
            if (j % onePerCent == 0) {
                progress = (int) j / onePerCent;
                setProgress(progress);
            }
            if (j % onePerMillion == 0) {
                publish(j);
            }
            //Thread.sleep(randomInt(1000, 5000));
        }
        

        return null;
    }

    private void publish(final long num) {
        this.txtArea.append(num + "\n");
        this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
    }

}

This is the main GUI after all:

Main Gui

and this is the execution:

Running without sleep

I just need to wait for a time on each WorkerDoSomething's task, adding that line (previously comment out): Thread.sleep(randomInt(1000, 5000));

but when I do.. the whole execution freezes, of course..because it uses a single thread to run all the tasks - I suppose.

Running with sleep

Is there a solution for this?

Oh...I have to use java 1.8 in the business:

java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

The whole project it's on my personnal git

--- first edit with debug perspective

debug

Upvotes: 3

Views: 183

Answers (2)

Abra
Abra

Reputation: 20914

Did you read the javadoc for class SwingWorker? Here is an excerpt:

Class SwingWorker<T,V>
Type Parameters:
T - the result type returned by this SwingWorker's doInBackground and get methods
V - the type used for carrying out intermediate results by this SwingWorker's publish and process methods

You correctly call method publish() in your doInBackground() method, but you have not overridden method process(). Refer to Tasks that Have Interim Results. Assuming that your publish() method is really supposed to be the process() method, then this should be the declaration of class WorkerDoSomething

public class WorkerDoSomething extends SwingWorker<Void, Long> {

And this should be method process() (instead of method publish())

protected void process(List<Long> nums) {
    this.txtArea.append(nums.get(nums.size() - 1) + "\n");
    this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
}

But doing that will not solve your problem. I can't explain why but the problem is resolved if you change the location of the call to Thread.sleep() in method doInBackground(). If I place the call to method sleep() immediately before the call to setProgress(), then the GUI does not "freeze". I got the idea to move the location of the call to sleep from this Web page, entitled Java Swing How to - Put lengthy task in SwingWorker

Here is my rewrite of your application. I took the liberty to rewrite parts of it, but essentially you just need to pay attention to the WorkerDoSomething class.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;

public class FormMain implements Runnable, ActionListener {
    private static final int  PANELS = 4;
    private static final String  START = "Start";

    private JFrame  frame;
    private PanelFoo[]  fooPanels;

    public FormMain() {
        fooPanels = new PanelFoo[PANELS];
        for (int i = 0; i < PANELS; i++) {
            fooPanels[i] = new PanelFoo();
        }
    }

    @Override // java.awt.event.ActionListener
    public void actionPerformed(ActionEvent event) {
        String actionCommand = event.getActionCommand();
        switch (actionCommand) {
            case START:
                start();
                break;
            default:
                JOptionPane.showMessageDialog(frame,
                                              actionCommand,
                                              "Unhandled",
                                              JOptionPane.WARNING_MESSAGE);
        }
    }

    @Override // java.lang.Runnable
    public void run() {
        createGui();
    }

    private JButton createButton(String text, int mnemonic, String tooltip) {
        JButton button = new JButton(text);
        button.setMnemonic(mnemonic);
        button.setToolTipText(tooltip);
        button.addActionListener(this);
        return button;
    }

    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(createButton(START, KeyEvent.VK_S, "Start your engines!"));
        return buttonsPanel;
    }

    private void createGui() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(createMainPanel(), BorderLayout.CENTER);
        frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createMainPanel() {
        JPanel mainPanel = new JPanel(new GridLayout(0, 4, 10, 10));
        for (PanelFoo pf : fooPanels) {
            mainPanel.add(pf);
        }
        return mainPanel;
    }

    private void start() {
        for (PanelFoo pf : fooPanels) {
            pf.start();
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new FormMain());
    }
}

class PanelFoo extends JPanel implements PropertyChangeListener {
    private static final long serialVersionUID = 4029040375048696554L;

    private JProgressBar  progressBar;
    private JTextArea  textArea;

    public PanelFoo() {
        super(new BorderLayout());
        textArea = new JTextArea(20, 10);
        JScrollPane scrollPane = new JScrollPane(textArea);
        add(scrollPane, BorderLayout.CENTER);
        progressBar = new JProgressBar(0, 100);
        add(progressBar, BorderLayout.PAGE_END);
    }

    public void appendText(String text) {
        textArea.append(text);
        textArea.append("\n");
    }

    @Override // java.beans.PropertyChangeListener
    public void propertyChange(PropertyChangeEvent evt) {
        if ("progress".equals(evt.getPropertyName())) {
            int progress = (Integer) evt.getNewValue();
            progressBar.setValue(progress);            
        }
    }

    public void start() {
        WorkerDoSomething worker = new WorkerDoSomething(this);
        worker.addPropertyChangeListener(this);
        worker.execute();
    }
}

class WorkerDoSomething extends SwingWorker<Void, Long> {
    private PanelFoo  fooPanel;
    private Random r = new Random();

    public WorkerDoSomething(PanelFoo pf) {
        fooPanel = pf;
    }

    @Override
    protected Void doInBackground() throws Exception {
        Integer randomNumber = randomInt(10000000, 1000000000);
        long j;
        int progress = 0;
        final int onePerCent = randomNumber / 100;
        final int onePerMillion = onePerCent / 10;
        for (j = 0; j <= randomNumber; j++) {
            if (j % onePerCent == 0) {
                try {
                    Thread.sleep(1000);
                }
                catch (InterruptedException xInterrupted) {
                    // Ignore
                }
                progress = (int) j / onePerCent;
                setProgress(progress);
            }
            if (j % onePerMillion == 0) {
                publish(j);
            }
        }
        return null;
    }

    @Override
    protected void process(List<Long> numbers) {
        Long number = numbers.get(numbers.size() - 1);
        fooPanel.appendText(String.valueOf(number));
    }

    private Integer randomInt(final int min, final int max) {
        final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;
        return randomNumber;
    }
}

I would say that technically, the above answers your question, which was:

Is there a solution for this?

I believe I have provided a solution, even if I can't explain why it works.

Upvotes: 2

Anmol Singh Jaggi
Anmol Singh Jaggi

Reputation: 8576

The execution is not freezing since Swing uses a thread pool of 10 threads to run the workers.
You can see it work properly if you comment out this part:

if (j % onePerMillion == 0) {
    publish(j);
}

PS - Why do you create a new SwingWorker in the actionPerformed method?
Why not simply write like this:

    this.btnStart.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
        w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
        final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
        w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
        final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
        w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
        final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
        w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

        w1.execute();
        w2.execute();
        w3.execute();
        w4.execute();
      }
    });

Upvotes: 2

Related Questions