2012-11-21 137 views
2

我目前正在學習Java中的多線程,並遇到了一個有趣的問題。 我有一個「裝載機」類讀取一些CSV文件。另一個線程正在運行時執行任務

public class LoaderThread implements Runnable{ 

@Override 
public void run(){ 
//do some fancy stuff 
} 
} 

此外我有一個SplashScreen,我想在數據加載時顯示。

import javax.swing.JLabel; 
import javax.swing.JWindow; 
import javax.swing.SwingConstants; 

public class SplashScreen extends JWindow{ 

JWindow jwin = new JWindow(); 

public SplashScreen(){ 

jwin.getContentPane().add(new JLabel("Loading...please wait!",SwingConstants.CENTER)); 
jwin.setBounds(200, 200, 200, 100); 

jwin.setLocationRelativeTo(null); 

jwin.setVisible(true); 

try { 
    Thread.sleep(3000); 
} catch (InterruptedException e) { 
    Thread.currentThread().interrupt(); 
} 

jwin.setVisible(false); 
jwin.dispose(); 

} 
} 

的代碼是從我的主類,當用戶點擊一個按鈕來運行:

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {           


    final Thread t = new Thread() { 

      @Override 
      public void run() { 

       LoaderThread myRunnable = new LoaderThread(); 
       Thread myThread = new Thread(myRunnable); 
       myThread.setDaemon(true); 
       myThread.start(); 
       while(myThread.isAlive()==true) 
       { 
        SplashScreen ss = new SplashScreen(); 
       } 


      } 
     }; 
     t.start(); // call back run() 
     Thread.currentThread().interrupt();   

}     

這種設置工作,但該消息被「閃」的時候,加載需要更長的時間超過3秒和顯示至少3秒,即使加載過程可能更短。

我現在想知道只要加載線程正在運行,是否可以顯示消息。不再更長,也不更短。

在此先感謝!

回答

5

這是一個完美的例子,觀察者模式可以很好地工作。在Swing中輕鬆實現這一點的一種方法是使用SwingWorker作爲後臺線程。在執行SwingWorker時顯示啓動畫面。在執行SwingWorker之前,向它添加一個PropertyChangeListener,並在它與SwingWorker.StateValue.DONE一起返回時,擺脫啓動畫面。

此外,不要在Swing事件線程上調用Thread.sleep(...),因爲這樣做是爲了保證災難。

編輯1
關於你的評論 -

你是什麼意思 「也不要叫了Thread.sleep Swing事件線程上......」 是什麼意思? 「你也不要在Swing事件線程上調用Thread.sleep ...」是什麼意思?

這被稱爲Swing事件線程上,否則被稱爲EDT或Ë發泄d ispatch 牛逼 hread:

try { 
    Thread.sleep(3000); 
} catch (InterruptedException e) { 
    Thread.currentThread().interrupt(); 
} 

這將會把你的整個應用程序的睡眠使其完全無響應,因此建議您永遠不要在EDT上致電Thread.sleep(...)

編輯2
示例代碼:

import java.awt.Dimension; 
import java.awt.event.ActionEvent; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 

import javax.swing.*; 

public class SwingWorkerEg extends JPanel { 
    private static final int PREF_W = 300; 
    private static final int PREF_H = 200; 

    public SwingWorkerEg() { 
     add(new JButton(new ButtonAction("Press Me"))); 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     return new Dimension(PREF_W, PREF_H); 
    } 

    private static void createAndShowGui() { 
     JFrame frame = new JFrame("SwingWorkerEg"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.getContentPane().add(new SwingWorkerEg()); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      createAndShowGui(); 
     } 
     }); 
    } 
} 

class ButtonAction extends AbstractAction { 
    public ButtonAction(String title) { 
     super(title); 
    } 

    @Override 
    public void actionPerformed(ActionEvent actEvt) { 
     final JButton source = (JButton)actEvt.getSource(); 
     source.setEnabled(false); 
     MySwingWorker mySw = new MySwingWorker(); 
     final MySplashScreen mySplash = new MySplashScreen(); 
     mySplash.setVisible(true); 

     mySw.addPropertyChangeListener(new PropertyChangeListener() { 

     @Override 
     public void propertyChange(PropertyChangeEvent pcEvt) { 
      if (SwingWorker.StateValue.DONE == pcEvt.getNewValue()) { 
       mySplash.setVisible(false); 
       mySplash.dispose(); 
       source.setEnabled(true); 
      } 
     } 
     }); 
     mySw.execute(); 
    } 
} 

class MySwingWorker extends SwingWorker<Void, Void> { 
    private static final long SLEEP_TIME = 5 * 1000; 

    @Override 
    protected Void doInBackground() throws Exception { 
     Thread.sleep(SLEEP_TIME); // emulate long-running task 
     return null; 
    } 
} 

class MySplashScreen extends JWindow { 
    private static final String LABEL_TEXT = "Loading, ... please wait..."; 
    private static final int PREF_W = 500; 
    private static final int PREF_H = 300; 

    public MySplashScreen() { 
     add(new JLabel(LABEL_TEXT, SwingConstants.CENTER)); 
     pack(); 
     setLocationRelativeTo(null); 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     return new Dimension(PREF_W, PREF_H); 
    } 
} 
+0

感謝,將有一個看的SwingWorker! 「你也不要在Swing事件線程上調用Thread.sleep ...」是什麼意思? – user1204121

+0

@ user1204121:請編輯1和編輯2. –

+0

運行完美 - 非常感謝! – user1204121

5

您正在循環中創建新實例,這就是它閃爍的原因。您寧願創建一個實例,使其可見,並且在其他線程完成執行時處置它。

+0

像這樣實施,它的工作原理! public void run(){ SplashScreen splashScreen = new SplashScreen(); LoaderThread myRunnable = new LoaderThread(); Thread myThread = new Thread(myRunnable); myThread.setDaemon(true); myThread.start(); 如果(myThread.isAlive()== FALSE) { splashScreen.dispose(); } } – user1204121

1

你可以使用一個顯示器http://en.wikipedia.org/wiki/Monitor_(synchronization

這樣,你可以確保當任務完成,而不是等待你的線程結束任意睡眠。

基本上你想這樣做(需要擴展):

public class Monitor { 
    private boolean task = false; 

    public void synchronized waitForTask(){ 
    while(!task){ 
     wait(); 
    } 
    } 

    public void synchronized taskIsDone(){ 
    task = true; 
    notifyAll(); 
    } 
} 

兩個對象都需要這種監控的參考。您的視圖必須調用waitForTask,並且您的任務將調用taskIsDone。

相關問題