How to share data with two(2) SwingWorker class in

2018-12-31 18:23发布

问题:

I have two SwingWorker class: FileLineCounterThread and FileDivisionThread

I will execute the two threads. When the lines counting thread finishes, it will pass the result to File Division thread.

I do not have an idea on how to pass the result to started thread.

回答1:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;

public class ExecutorAndSwingWorker2 {

    private JFrame frame = new JFrame();
    private JButton button1;
    private JButton button2;
    private JButton button3;
    private JButton button4;
    private JPanel buttonPanel = new JPanel();
    private Executor executor = Executors.newCachedThreadPool();
    private javax.swing.Timer timer1;
    private javax.swing.Timer timer2;
    private javax.swing.Timer timer3;
    private javax.swing.Timer timer4;
    private Random random = new Random();

    public ExecutorAndSwingWorker2() {
        button1 = new JButton(\"  Executor + SwingWorker Thread No.1  \");
        button1.setFocusable(false);
        button2 = new JButton(\"  Executor + SwingWorker Thread No.2  \");
        button3 = new JButton(\"  Executor + SwingWorker Thread No.3  \");
        button4 = new JButton(\"  Executor + SwingWorker Thread No.4  \");
        buttonPanel = new JPanel();
        buttonPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
        buttonPanel.setLayout(new GridLayout(2, 2, 20, 20));
        buttonPanel.add(button1);
        buttonPanel.add(button2);
        buttonPanel.add(button3);
        buttonPanel.add(button4);
        frame.setTitle(\"Shaking Button Demo\");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(buttonPanel);
        frame.setPreferredSize(new Dimension(700, 170));
        frame.setLocation(150, 100);
        frame.pack();
        frame.setVisible(true);
        executor.execute(new ExecutorAndSwingWorker2.MyTask(\"startButton1\")); // non on EDT
    }

    private void startButton1() {
        System.out.println(\"Starting long Thread == startButton1()\");
        try {
            Thread.sleep(15000);
        } catch (InterruptedException ex) {
        }
    }

    private void startButton2() {
        System.out.println(\"Starting long Thread == startButton2()\");
        try {
            Thread.sleep(17500);
        } catch (InterruptedException ex) {
        }
    }

    private void startButton3() {
        System.out.println(\"Starting long Thread == startButton3()\");
        try {
            Thread.sleep(12500);
        } catch (InterruptedException ex) {
        }
    }

    private void startButton4() {
        System.out.println(\"Starting long Thread == startButton4()\");
        try {
            Thread.sleep(20000);
        } catch (InterruptedException ex) {
        }
    }

    private void colorAction1() {
        timer1 = new Timer(1000, new AbstractAction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                random = new Random();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        button1.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
                        button1.validate();
                        button1.repaint();
                    }
                });
            }
        });
        timer1.setDelay(500);
        timer1.setRepeats(true);
        timer1.start();
    }

    private void colorAction2() {
        timer2 = new Timer(1200, new AbstractAction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                random = new Random();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        button2.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
                        button2.validate();
                        button2.repaint();
                    }
                });
            }
        });
        timer2.setDelay(500);
        timer2.setRepeats(true);
        timer2.start();
    }

    private void colorAction3() {
        timer3 = new Timer(1400, new AbstractAction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                random = new Random();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        button3.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
                        button3.validate();
                        button3.repaint();
                    }
                });
            }
        });
        timer3.setDelay(500);
        timer3.setRepeats(true);
        timer3.start();
    }

    private void colorAction4() {
        timer4 = new Timer(1600, new AbstractAction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                random = new Random();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        button4.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
                        button4.validate();
                        button4.repaint();
                    }
                });
            }
        });
        timer4.setDelay(500);
        timer4.setRepeats(true);
        timer4.start();
    }

    private void endButton1() {
        timer1.stop();
        button1.setBackground(null);
        System.out.println(\"Long Thread Ends == startButton1()\");
        executor.execute(new ExecutorAndSwingWorker2.MyTask(\"startButton3\")); // non on EDT
    }

    private void endButton2() {
        timer2.stop();
        button2.setBackground(null);
        System.out.println(\"Long Thread Ends == startButton2()\");
    }

    private void endButton3() {
        timer3.stop();
        button3.setBackground(null);
        System.out.println(\"Long Thread Ends == startButton3()\");
        executor.execute(new ExecutorAndSwingWorker2.MyTask(\"startButton2\")); // non on EDT
        executor.execute(new ExecutorAndSwingWorker2.MyTask(\"startButton4\")); // non on EDT
    }

    private void endButton4() {
        timer4.stop();
        button4.setBackground(null);
        System.out.println(\"Long Thread Ends == startButton4()\");
        executor.execute(new ExecutorAndSwingWorker2.MyTask(\"startButton1\")); // non on EDT
    }

    private class MyTask extends SwingWorker<Void, Integer> {

        private String str;
        private String namePr;
        private JDialog dialog = new JDialog();

        MyTask(String str) {
            this.str = str;
            addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog, str, namePr));
        }

        @Override
        protected Void doInBackground() throws Exception {
            if (str.equals(\"startButton1\")) {
                colorAction1();
                startButton1();
            } else if (str.equals(\"startButton2\")) {
                colorAction2();
                startButton2();
            } else if (str.equals(\"startButton3\")) {
                colorAction3();
                startButton3();
            } else if (str.equals(\"startButton4\")) {
                colorAction4();
                startButton4();
            }
            return null;
        }

        @Override
        protected void process(List<Integer> progress) {
            System.out.println(str + \" \" + progress.get(progress.size() - 1));
        }

        @Override
        protected void done() {
            if (str.equals(\"startButton1\")) {
                endButton1();
            } else if (str.equals(\"startButton2\")) {
                endButton2();
            } else if (str.equals(\"startButton3\")) {
                endButton3();
            } else if (str.equals(\"startButton4\")) {
                endButton4();
            }
        }
    }

    private class SwingWorkerCompletionWaiter implements PropertyChangeListener {

        private JDialog dialog;
        private String str;
        private String namePr;

        SwingWorkerCompletionWaiter(JDialog dialog, String str, String namePr) {
            this.dialog = dialog;
            this.str = str;
            this.namePr = namePr;
        }

        @Override
        public void propertyChange(PropertyChangeEvent event) {
            if (\"state\".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
                System.out.println(\"Thread Status with Name :\" + str + \", SwingWorker Status is \" + event.getNewValue());
            } else if (\"state\".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
                System.out.println(\"Thread Status with Mame :\" + str + \", SwingWorker Status is \" + event.getNewValue());
            } else if (\"state\".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
                System.out.println(\"Thread Status with Name :\" + str + \", SwingWorker Status is \" + event.getNewValue());
            } else {
                System.out.println(\"Thread Status with Name :\" + str + \", Something wrong happends \");
            }
        }
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                ExecutorAndSwingWorker2 executorAndSwingWorker = new ExecutorAndSwingWorker2();
            }
        });
    }
}


回答2:

SwingWorker.execute() is buggy and will only execute tasks serially. Use ExecutorService.execute() for concurrency:

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RunnableFuture;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;

public class MyFrame extends JFrame implements ActionListener {

    /**
     * Test Driver
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MyFrame frame = new MyFrame(\"Swing Concurrency Test\");
                frame.setVisible(true);
            }
        });
    }

    /**
     * Thread Executor
     * (must be explicitly shutdown, see WindowAdapter below)
     */
    private final ExecutorService exec = Executors.newFixedThreadPool(2);

    /**
     * Button action
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        button.setEnabled(false);
        textArea.append(\"\\nStarting both tasks...\\n\");

        // start both tasks, pass a reference to outer task
        FileLineCounterThread counterTask = new FileLineCounterThread();
        exec.execute(counterTask);
        FileDivisionThread divisionTask = new FileDivisionThread(counterTask);
        exec.execute(divisionTask);
    }

    /**
     * Counter task
     */
    private class FileLineCounterThread extends SwingWorker<Long, String> {
        private String template = \"[FileLineCounterThread] %s\\n\";

        @Override
        protected Long doInBackground() throws Exception {
            // do some work
            publish(\"started...\");
            Thread.sleep(10000);

            // return the result
            return 42L;
        }

        @Override
        protected void process(List<String> chunks) {
            for (String chunk : chunks) {
                textArea.append(String.format(template, chunk));
            }
        }

        @Override
        protected void done() {
            try {
                textArea.append(String.format(
                        template, \"complete.  Counted: \" + get()));
            }
            catch (Exception e) {
                // catch any exceptions thrown during execution
                e.printStackTrace();
            }
        }
    }

    /**
     * File Division task 
     */
    private class FileDivisionThread extends SwingWorker<String, String> {
        private RunnableFuture<Long> counterTask;
        private String template = \"    [FileDivisionThread] %s\\n\";

        public FileDivisionThread(RunnableFuture<Long> counterTask) {
            this.counterTask = counterTask;
        }

        @Override
        protected String doInBackground() throws Exception {
            // do some initial work
            publish(\"started...\");
            Thread.sleep(2000);

            // wait for other task to complete and get result
            publish(\"Waiting for line counter to finish...\");
            long numLines = counterTask.get(); 
            publish(\"Line count received: \" + numLines);

            // do the rest of the work and return result
            Thread.sleep(5000);
            return \"complete.\";
        }

        @Override
        protected void process(List<String> chunks) {
            for (String chunk : chunks) {
                textArea.append(String.format(template, chunk));
            }
        }

        @Override
        protected void done() {
            try {
                textArea.append(String.format(template, get()));
                button.setEnabled(true);
            }
            catch (Exception e) {
                // catch any exceptions thrown during execution
                e.printStackTrace();
            }
        }
    }

    /////////////////////////
    //// GUI Boilerplate ////
    /////////////////////////

    private JScrollPane scroller = new JScrollPane();
    private JTextArea textArea = new JTextArea();
    private JButton button = new JButton(\"Start\");

    public MyFrame(String windowTitle) {
        super(windowTitle);
        initComponents();
    }

    private void initComponents() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                exec.shutdownNow();
                System.exit(0);
            }
        });
        button = new JButton(\"Start\");
        button.addActionListener(this);
        textArea = new JTextArea();
        textArea.setColumns(35);
        textArea.setRows(15);
        scroller.setViewportView(textArea);

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        getContentPane().setLayout(new GridBagLayout());

        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.insets = new Insets(10, 0, 0, 0);
        getContentPane().add(button, gridBagConstraints);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new Insets(10, 10, 10, 10);
        getContentPane().add(scroller, gridBagConstraints);

        pack();
    }
}


回答3:

PipedReader/Writer for character data & PipedInput/OutputStream for binary data

in java.io.

Regards, Stéphane



回答4:

never hands up, never surrender its possible with Executor and SwingWorker

1/ bug for Executor and SwingWorker

2/ hold and check number of thread started by Executor and live SwingWorkers threads with intentions to avoid caught above mentioned bug

3/ check maximum numbers for Executor or restict that to final munber

EDIT changed by OP\'s requirements

import java.beans.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class ExecutorAndSwingWorker1 {

    private static Executor executor = Executors.newCachedThreadPool();

    private static void startButton1() {
        System.out.println(\"Starting long Tread == startButton1()\");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
        }
    }

    private static void startButton2() {
        System.out.println(\"Starting long Tread == startButton2()\");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
        }
    }

    private static void startButton3() {
        System.out.println(\"Starting long Tread == startButton3()\");
        try {
            Thread.sleep(1500);
        } catch (InterruptedException ex) {
        }
    }

    private static void startButton4() {
        System.out.println(\"Starting long Tread == startButton4()\");
        try {
            Thread.sleep(500);
        } catch (InterruptedException ex) {
        }
    }

    private static void endButton1() {
        System.out.println(\"Long Tread Ends == startButton1()\");
        executor.execute(new ExecutorAndSwingWorker1.MyTask(\"startButton3\")); // non on EDT
    }

    private static void endButton2() {
        System.out.println(\"Long Tread Ends == startButton2()\");
        executor.execute(new ExecutorAndSwingWorker1.MyTask(\"startButton4\")); // non on EDT
    }

    private static void endButton3() {
        System.out.println(\"Long Tread Ends == startButton3()\");
    }

    private static void endButton4() {
        System.out.println(\"Long Tread Ends == startButton3()\");
    }

    private static class MyTask extends SwingWorker<Void, Integer> {

        private String str;
        private String namePr;
        private JDialog dialog = new JDialog();

        MyTask(String str) {
            this.str = str;
            addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog, str, namePr));
        }

        @Override
        protected Void doInBackground() throws Exception {
            if (str.equals(\"startButton1\")) {
                startButton1();
            } else if (str.equals(\"startButton2\")) {
                startButton2();
            } else if (str.equals(\"startButton3\")) {
                startButton3();
            } else if (str.equals(\"startButton4\")) {
                startButton4();
            }
            return null;
        }

        @Override
        protected void process(List<Integer> progress) {
            System.out.println(str + \" \" + progress.get(progress.size() - 1));
        }

        @Override
        protected void done() {
            if (str.equals(\"startButton1\")) {
                endButton1();
            } else if (str.equals(\"startButton2\")) {
                endButton2();
            } else if (str.equals(\"startButton3\")) {
                endButton3();
            } else if (str.equals(\"startButton4\")) {
                endButton4();
            }
        }
    }

    private static class SwingWorkerCompletionWaiter implements PropertyChangeListener {

        private JDialog dialog;
        private String str;
        private String namePr;

        SwingWorkerCompletionWaiter(JDialog dialog, String str, String namePr) {
            this.dialog = dialog;
            this.str = str;
            this.namePr = namePr;
        }

        @Override
        public void propertyChange(PropertyChangeEvent event) {
            if (\"state\".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
                System.out.println(\"Thread Status with Name :\" + str + \", SwingWorker Status is \" + event.getNewValue());
            } else if (\"state\".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
                System.out.println(\"Thread Status with Mame :\" + str + \", SwingWorker Status is \" + event.getNewValue());
            } else if (\"state\".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
                System.out.println(\"Thread Status with Name :\" + str + \", SwingWorker Status is \" + event.getNewValue());
            } else {
                System.out.println(\"Thread Status with Name :\" + str + \", Something wrong happends \");
            }
        }
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                executor.execute(new ExecutorAndSwingWorker1.MyTask(\"startButton1\")); // non on EDT
                executor.execute(new ExecutorAndSwingWorker1.MyTask(\"startButton2\")); // non on EDT
            }
        });
    }

    private ExecutorAndSwingWorker1() {
    }
}


回答5:

I am not sure this is a solution you should use, and it undermines the simplicity and safety you get from using SwingWorker, but I\'ll mention it for completeness.

Put two fields where both threads can see them: one boolean, called hasValue, initialized to false, and one int (or long) called countValue. Both must be declared as volatile. When the counter thread is done, put the count in countValue. Then set hasValue to true. The division thread can then check `hasValue\' periodically and grab the count when it is available.

If the division is providing values that will be more accurate once it gets the count, this will do. More likely, it is doing some work, then waiting for the count. In this case, set up a third field called countMonitor, defined as final Object. When it finishes the initial work, have it check hasValue. If it\'s true, grab the value and continue. If it\'s false, call the wait method on countMonitor and continue when notified. The counter thread, when done, should always call the notifyAll method on countMonitor after putting values in hasValue and countValue.

I\'ve left out a bit here. The javadoc for Object will tell you about needed synchronization and checked exceptions. Your design is straightforward enough that you won\'t be troubled with the usual supernatural horror stories multi-threading generates. I hope. But you might want to do a bit of research if you go this route. (If you repeat the whole process in the same session, you will definitely want to do a lot of research.)