我正在使用swing制作一个简单的Java游戏,并且在按下按钮后,我的GUI会偶尔出现问题(很可能是由于线程问题)。应该触发JPanels中的切换。
我发布了一个相关的帖子here,其中有关于我目前正在使用的实际代码的更多详细信息(虽然我确实更新了倒计时并使其工作正常)。从该线程的答案来看,似乎使用SwingUtilities.invokeLater()
或invokeAndWait()
可能是我解决问题所需要的,但我不确定我的代码在哪里是必要的或者究竟如何实现它
我对线程知之甚少,可以使用任何帮助(最好是有些详细,并附带一些示例代码)。如果有任何进一步的细节有用,请告诉我。
答案 0 :(得分:3)
请参阅:Tutorial: Concurrency in Swing
一般来说,Event Dispatch Thread是一个单一的线程,通过事件队列,一次处理一个。
SwingUtilities.invokeLater(..)
将Runnable放在此队列中。因此当EDT完成队列之前的所有事情时,它将由EDT处理(这就是为什么在队列上睡觉会阻止其他事件,如重新绘制)。从EDT本身调用invokeLater(..)是相对不常见的,尽管有些情况下它很有用(通常是作为黑客)。在过去的6年里,我认为我没有合法使用SwingUtilities.invokeAndWait(..)。也许一次。
javax.swing.Timer
可以配置为触发一次或定期触发。当它触发时,它会在EDT队列上放置一个事件。如果您需要完成计算密集型处理,请考虑使用javax.swing.SwingWorker
在另一个线程上进行计算,并以线程安全的方式返回结果(这也相对较少)。
答案 1 :(得分:0)
值得注意的是docs。在您的情况下,这解释了SwingUtilities.invokeLater()
如何工作以及在何处使用它:
导致doRun.run()在AWT事件上异步执行 调度线程。 应用程序时应使用此方法 线程需要更新GUI 。
因此,在修改GUI的操作中,必须使用invokeLater
方法确保GUI不会冻结。
另一个很好的资源是Java教程。它们涵盖concurrency in Swing。
答案 2 :(得分:0)
如果您在GUI代码中定义了一些工作
Runnable doWorkRunnable = new Runnable() {
@Override
public void run() {
doWork();
}
};
您通过将其附加到新的Thread
Thread t = new Thread(doWorkRunnable);
t.start();
您正在GUI线程中执行您的工作,这将导致Swing应用程序出现问题。
相反尝试这个(让我提一下这只是一个使用示例)
SwingUtilities.invokeLater(doWorkRunnable);
这会将您的Runnable
工作人员置于AWT事件队列中,并在以前的事件结束时执行。
编辑:这是一个完整的例子,它从3到0执行倒计时,然后在倒计时后做你想做的任何事情。
public class TestFrame extends JFrame {
private JPanel contentPane;
private final Timer timer;
private TimerTask[] tasks;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestFrame frame = new TestFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TestFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
final JLabel lblCountdown = new JLabel();
contentPane.add(lblCountdown, BorderLayout.NORTH);
JButton btnStart = new JButton("Start");
contentPane.add(btnStart, BorderLayout.SOUTH);
timer = new Timer();
tasks = new TimerTask[4];
setContentPane(contentPane);
for (int i = 0; i < 4; i++) {
final int count = i;
tasks[i] = new TimerTask() {
public void run() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
lblCountdown.setText(count + "");
}
});
}
};
}
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 4; i++) {
timer.schedule(tasks[4 - i - 1], (1000 * i), (1000 * (i + 1)));
}
// add another timer.schedule(TimerTask)
// to execute that "move to game screen" task
TimerTask taskGotoGame = new TimerTask() {
public void run() {
timer.cancel();
JOptionPane.showMessageDialog(null, "Go to game", "Will now", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
};
// and schedule it to happen after ROUGHLY 3 seconds
timer.schedule(taskGotoGame, 3000);
}
});
}
}
答案 3 :(得分:0)
我创建了一个WorkerThread类来处理Threads和GUI current / main thread。我已经将我的GUI应用程序放在WorkerThread的construct()方法中,当一个事件触发启动XXXServer然后所有线程都被激活并且GUI工作平稳地没有冻结。看一看。 / ** *行动事件 * * @see java.awt.event.ActionListener #actionPerformed(java.awt.event.ActionEvent) * / public void actionPerformed(ActionEvent ae){ log.info(“actionPerformed begin ...”+ ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the XXXServer ...");
// initializing the Socket Server
try {
XXXServer xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}
答案 4 :(得分:0)
为了在现有WorkerThread中调用动作,可以使用SwingUtilities.invokeLater()直观地将用户定义的事件发送给JFrame的actionPerformed()方法,
>>> dictionary = {
... "Name": "Eva",
... "Surname": "Schwartz",
... "babies": [
... {
... "Name": "Klara",
... "age": 6
... },
... {
... "Name": "Nikola",
... "age": 8
... }
... ]
... }
>>> baby_names = [baby["Name"] for baby in dictionary["babies"]]
>>> baby_names
['Klara', 'Nikola']
现在,任何线程中调用的TestFrame.invokeLater()将在现有WorkerThread中的TestFrame.actionPerformed()中进行处理。