【问题标题】:Performing a task while another thread is running在另一个线程运行时执行任务
【发布时间】:2012-11-21 21:06:50
【问题描述】:

我目前正在学习 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 秒时,消息“闪烁”,即使加载过程可能更短。

我现在想知道只要加载线程正在运行,是否可以显示消息。不长也不短。

提前致谢!

【问题讨论】:

    标签: java multithreading swing


    【解决方案1】:

    这是观察者模式可以很好地工作的完美示例。在 Swing 中轻松做到这一点的一种方法是使用 SwingWorker 作为后台线程。执行 SwingWorker 时显示启动屏幕。在执行 SwingWorker 之前,给它添加一个 PropertyChangeListener,当它返回 SwingWorker.StateValue.DONE 时,摆脱闪屏。

    另外,不要像你正在做的那样在 Swing 事件线程上调用 Thread.sleep(...),因为这是灾难的保证。

    编辑 1
    关于您的评论--

    “也不要在 Swing 事件线程上调用 Thread.sleep...”是什么意思? “也不要在 Swing 事件线程上调用 Thread.sleep...”是什么意思?

    这是在 Swing 事件线程上调用的,也称为 EDT 或 Event Dispatch T线程:

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

    【讨论】:

    • 谢谢,看看 SwingWorker! “也不要在 Swing 事件线程上调用 Thread.sleep ...”是什么意思?
    【解决方案2】:

    您正在循环中创建新实例,这就是它闪烁的原因。您宁愿创建一个实例,使其可见,当另一个线程执行完毕后,将其释放。

    【讨论】:

    • 像这样实现,它的工作原理! public void run() { SplashScreen splashScreen = new SplashScreen(); LoaderThread myRunnable = new LoaderThread();线程 myThread = new Thread(myRunnable); myThread.setDaemon(true); myThread.start(); if(myThread.isAlive()==false) { splashScreen.dispose(); } }
    【解决方案3】:

    您可以使用监视器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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      • 2014-06-21
      • 2020-10-15
      • 1970-01-01
      • 2020-12-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多