2012-11-21 97 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。

相关问题