我目前正在学习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秒时,该消息会“闪烁”,即使加载过程可能会更短。
我现在想知道只要加载线程正在运行,是否可以显示消息。不再也不短。
提前致谢!
答案 0 :(得分:5)
您正在循环中创建新实例,这就是它闪烁的原因。你宁愿创建一个实例,让它可见,当另一个线程完成执行时,处理它。
答案 1 :(得分:5)
这是观察者模式运作良好的完美示例。在Swing中轻松完成此操作的一种方法是使用SwingWorker作为后台线程。执行SwingWorker时显示启动画面。在执行SwingWorker之前,向它添加一个PropertyChangeListener,当它返回SwingWorker.StateValue.DONE时,摆脱启动画面。
此外,不要像你正在做的那样在Swing事件线程上调用Thread.sleep(...)
,因为这是灾难的保证。
编辑1
关于你的评论 -
你是什么意思“也不要在Swing事件线程上调用Thread.sleep ......”?你是什么意思“也不要在Swing事件线程上调用Thread.sleep ......”?
这是在Swing事件线程上调用的,也称为EDT或 E vent D ispatch T 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);
}
}
答案 2 :(得分: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。