Swing Worker和普通线程之间的差异



根据我之前的问题What';拥有一个动态JTable的最佳方式是,它的数据会随着数据库表中的变化而更新,我使用了以下代码自动更新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);

但我读过关于SwingWorker的文章,这似乎是正确的方法。SwingWorkers的正常实现有doInBackground、publish和done三种受保护的方法。我还尝试了SwingWorker的另一个版本,即

    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);
}

哪种方法更好?此外,jList每5秒闪烁5次,我该如何避免这种情况?

Swing Timer在事件调度线程的上下文中执行其通知,使其适合更新UI,但不适合执行长时间运行或块任务(如查询数据库)

Thread适用于执行长时间运行或块任务,但不应用于更新UI,因为Swing不是线程安全的,并且只能在EDT的上下文中修改UI。当然你可以使用EventQueue.invokeLater,但如果你需要为这些调用提供参数,这会变得很痛苦。。。

您可以使用SwingWorker,它能够在后台(EDT之外)执行长时间运行或阻塞任务,但提供了更新UI的方法,通过它的publish/processdone和/或PropertyChange支持

看看:

  • Swing中的并发
  • 如何使用摆动计时器
  • Worker线程和SwingWorker

有关更多详细信息,

哪种方法更好

你的第一个例子违反了Swing的单线程规则,所以它没有多大用处。。。

您的第二个例子是overkill,TimerTask已经在后台运行,实际上您最好使用ScheduledExecutorService并将SwingWorker的实例传递给它。。。

更新

这不是我希望的方式,但是。。。

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);
            }
        }
    }
}

最新更新