user2575609
user2575609

Reputation: 31

Difference between Swing Worker and normal Threads?

As per my previous question What's the best way to have a dynamic JTable whose data updates itself with changes in database table? , I have used the following code for an automatically updating jList

       DefaultListModel x = new DefaultListModel();
       jList1.setModel(x);
       int initialDelay = 0;
       int period = 5000;
       Timer timer = new Timer();
       TimerTask task = new TimerTask() {

        public void run() {
            x.clear();
            try {
                conn = Database.Connect("patient.db");
                sql = "SELECT * FROM PATIENT_APPOINTMENTS";
                pst = conn.prepareStatement(sql);
                rs = pst.executeQuery();

                while(rs.next()) {

                    x.addElement(rs.getString("patientid"));

                }


            }
            catch ( Exception e) {

                JOptionPane.showMessageDialog(null,e,"Error",JOptionPane.ERROR_MESSAGE);
            }
        }
    };
    timer.scheduleAtFixedRate(task,initialDelay,period);

But I have read about SwingWorker and it seems to be the right way to do it .A normal implementation of swing worker has 3 protected methods of doInBackground , publish and done . I also tried another version with SwingWorker which is

    DefaultListModel x = new DefaultListModel();
    jList1.setModel(x);

    int initialDelay = 0;
    int period = 5000;
    Timer timer = new Timer();
    TimerTask task = new TimerTask() {

        public void run() {
            x.clear();

            try {

                SwingWorker<ArrayList<String>, Void> worker = new SwingWorker<ArrayList<String>, Void>() {

                    protected ArrayList<String> doInBackground() throws Exception {

                        ArrayList<String> patientid = new ArrayList<String>();
                        conn = Database.Connect("patient.db");
                        sql = "SELECT * FROM PATIENT_APPOINTMENTS";
                        pst = conn.prepareStatement(sql);
                        rs = pst.executeQuery();

                        while (rs.next()) {

                            patientid.add(rs.getString("patientid"));

                        }

                        return patientid;
                    }

                    protected void done() {

                        ArrayList<String> id = new ArrayList<String>();
                        try {

                            id = get();
                            for (int i = 0; i < id.size(); i++) {
                                x.addElement(id.get(i));
                            }

                        } catch (InterruptedException e) {

                            JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE);
                        } catch (ExecutionException e) {
                            JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE);
                        }
                    }

                };
                worker.execute();
            } catch (Exception e) {

                JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE);
            }

        }
    };
    timer.scheduleAtFixedRate(task, initialDelay, period);
}

Which is the better method ? Further , the jList blinks once 5 every 5 seconds , how can I avoid that ?

Upvotes: 1

Views: 2067

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347194

Swing Timer executes it's notifications within the context of the Event Dispatching Thread, making it suitable for updating the UI, but not suitable for executing long running or block tasks (like querying a database)

Threads are suitable for executing long running or block tasks but should never be used for updating the UI, as Swing is not thread safe and the UI should only be modified from within the context of the EDT. Sure you can use EventQueue.invokeLater, but it becomes painful if you need to supply parameters to those calls...

You could use a SwingWorker instead, which has the capacity to execute long running or blocking tasks in the background (off the EDT), but provides means by which you can update the UI, via it's publish/process, done and/or PropertyChange support

Take a look at:

for more details

Which is the better method

Your first example violates the single thread rules of Swing, so it ain't much use...

Your second example is over kill, the TimerTask is already running the background, you'd actually be better of using a ScheduledExecutorService and passing it an instance of the SwingWorker...

Updated

Not the way I would have liked it to work, but...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel label;
        private int count;
        private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);

        public TestPane() {
            setLayout(new GridBagLayout());
            label = new JLabel("...");
            add(label);

            service.schedule(new CounterWorker(), 1, TimeUnit.SECONDS);
        }

        public class CounterWorker extends SwingWorker<Integer, Integer> {

            @Override
            protected Integer doInBackground() throws Exception {
                System.out.println("Tick");
                for (int index = 0; index < 100; index++) {
                    count++;
                    publish(count);
                    Thread.yield();
                }
                return count;
            }

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

            @Override
            protected void done() {
                service.schedule(new CounterWorker(), 1, TimeUnit.SECONDS);
            }

        }

    }

}

Upvotes: 6

Related Questions