使用SwingWorker的Java Swing中的多线程
如果要使用Swing用J??ava編寫桌面或Java Web Start程序,您可能會覺得需要通過創建自己的線程在后臺運行某些東西。
沒有什么可以阻止您在Swing中使用標準的多線程技術,并且需要遵循通常的注意事項。 如果您有多個線程訪問相同的變量,則需要使用同步方法或代碼塊(或諸如AtomicInteger或ArrayBlockingQueue之類的線程安全類)。
但是,對于那些粗心的人來說是一個陷阱。 與大多數用戶界面API一樣,您無法從自己創建的線程更新用戶界面。 好吧,正如每個Java本科生都知道的那樣,您通常可以 ,但是您不應該。 如果這樣做,有時您的程序會運行,而其他時候則無法。
您可以通過使用專門的SwingWorker類來解決此問題。 在本文中,我將向您展示即使您正在使用Thread類,如何使程序正常運行,然后我們將繼續研究SwingWorker解決方案。
為了演示,我創建了一個Swing程序。
如您所見,它由兩個標簽和一個開始按鈕組成。 此刻,單擊開始按鈕將調用一個不執行任何操作的處理程序方法。 這是Java代碼:
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.List; import java.util.concurrent.ExecutionException;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 statusLabel = new JLabel('Task not completed.');private JButton startButton = new JButton('Start');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(statusLabel, gc);gc.gridx = 0;gc.gridy = 2;gc.weightx = 1;gc.weighty = 1;add(startButton, gc);startButton.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent arg0) {start();}});setSize(200, 400);setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);}private void start() {}public static void main(String[] args) {SwingUtilities.invokeLater(new Runnable() {@Overridepublic void run() {new MainFrame('SwingWorker Demo');}});} }我們將添加一些代碼到start()方法中,以響應單擊開始按鈕而調用。
首先讓我們嘗試一個普通線程。
private void start() {Thread worker = new Thread() {public void run() {// Simulate doing something useful.for(int i=0; i<=10; i++) {// Bad practicecountLabel1.setText(Integer.toString(i));try {Thread.sleep(1000);} catch (InterruptedException e) {}}// Bad practicestatusLabel.setText('Completed.');}};worker.start();}實際上,此代碼似乎有效(至少對我而言)。 該程序最終看起來像這樣:
但是,不建議您這樣做。 我們正在從自己的線程中更新GUI,在某些情況下,這肯定會引發異常。
如果要從另一個線程更新GUI,則應使用SwingUtilities安排更新代碼在事件分發線程上運行。
以下代碼很好,但像惡魔本人一樣丑陋。
private void start() {Thread worker = new Thread() {public void run() {// Simulate doing something useful.for(int i=0; i<=10; i++) {final int count = i;SwingUtilities.invokeLater(new Runnable() {public void run() {countLabel1.setText(Integer.toString(count));}});try {Thread.sleep(1000);} catch (InterruptedException e) {}}SwingUtilities.invokeLater(new Runnable() {public void run() {statusLabel.setText('Completed.');}});}};worker.start();}當然,必須做些什么使我們的代碼更優雅?
SwingWorker類
SwingWorker是使用Thread class (專門為Swing設計)的替代方法。 這是一個抽象類,它帶有兩個模板參數,這使它看起來非常兇猛,并使大多數人不愿使用它。 但是實際上,它并不像看起來那樣復雜。
讓我們看一些僅運行后臺線程的代碼。 對于第一個示例,我們將不使用任何一個模板參數,因此將它們都設置為Void ,這是Java的等效于原始void類型的類(帶有小寫的“ v”)。
運行后臺任務
通過實現doInBackground方法并調用execute來運行代碼,我們可以在后臺運行任務。
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {@Overrideprotected Void doInBackground() throws Exception {// Simulate doing something useful.for (int i = 0; i <= 10; i++) {Thread.sleep(1000);System.out.println('Running ' + i);}return null;}};worker.execute();請注意, SwingWorker是一站式服務,因此,如果我們想再次運行代碼,則需要創建另一個SwingWorker ; 您無法重新啟動同一臺。
很簡單,嘿? 但是,如果我們想在運行代碼后以某種狀態更新GUI,該怎么辦? 您無法從doInBackground更新GUI,因為它不在主事件分配線程中運行。
但是有一個解決方案。 我們需要利用第一個模板參數。
線程完成后更新GUI
我們可以通過從doInBackground()返回一個值,然后doInBackground() done()來更新GUI,從而可以安全地更新GUI。 我們使用get()方法檢索從doInBackground()返回的值
因此,第一個模板參數確定doInBackground()和get()的返回類型。
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {@Overrideprotected Boolean doInBackground() throws Exception {// Simulate doing something useful.for (int i = 0; i <= 10; i++) {Thread.sleep(1000);System.out.println('Running ' + i);}// Here we can return some object of whatever type// we specified for the first template parameter.// (in this case we're auto-boxing 'true').return true;}// Can safely update the GUI from this method.protected void done() {boolean status;try {// Retrieve the return value of doInBackground.status = get();statusLabel.setText('Completed with status: ' + status);} catch (InterruptedException e) {// This is thrown if the thread's interrupted.} catch (ExecutionException e) {// This is thrown if we throw an exception// from doInBackground.}}};worker.execute();如果我們要在進行過程中更新GUI怎么辦? 這就是第二個模板參數的用途。
從正在運行的線程更新GUI
要從正在運行的線程更新GUI,我們使用第二個模板參數。 我們調用publish()方法來“發布”我們要用來更新用戶界面的值(可以是第二個模板參數指定的任何類型)。 然后,我們重寫process()方法,該方法接收我們發布的值。
實際上process()接收已發布值的列表,因為在實際調用process()之前可能會發布多個值。
在此示例中,我們只是將最新值發布到用戶界面。
SwingWorker<Boolean, Integer> worker = new SwingWorker<Boolean, Integer>() {@Overrideprotected Boolean doInBackground() throws Exception {// Simulate doing something useful.for (int i = 0; i <= 10; i++) {Thread.sleep(1000);// The type we pass to publish() is determined// by the second template parameter.publish(i);}// Here we can return some object of whatever type// we specified for the first template parameter.// (in this case we're auto-boxing 'true').return true;}// Can safely update the GUI from this method.protected void done() {boolean status;try {// Retrieve the return value of doInBackground.status = get();statusLabel.setText('Completed with status: ' + status);} catch (InterruptedException e) {// This is thrown if the thread's interrupted.} catch (ExecutionException e) {// This is thrown if we throw an exception// from doInBackground.}}@Override// Can safely update the GUI from this method.protected void process(List<Integer> chunks) {// Here we receive the values that we publish().// They may come grouped in chunks.int mostRecentValue = chunks.get(chunks.size()-1);countLabel1.setText(Integer.toString(mostRecentValue));}};worker.execute();?
我希望您喜歡這個對高度有用的SwingWorker類的介紹。
您可以在我的網站Cave of Programming中找到更多教程,包括有關多線程的完整免費視頻課程以及有關Swing,Android和Servlets 的課程 。
參考:來自Java出現日歷博客的JCG合作伙伴 John Purcell的SwingWorker與Java Swing中的多線程 。
翻譯自: https://www.javacodegeeks.com/2012/12/multi-threading-in-java-swing-with-swingworker.html
總結
以上是生活随笔為你收集整理的使用SwingWorker的Java Swing中的多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 农行债市宝收益计算方法?
- 下一篇: 封闭式理财忘记利率怎么算利息?