我读到构造Swing组件和处理事件的所有代码必须由Event Dispatch Thread运行.我理解这是如何通过使用该SwingUtilities.invokeLater()方法完成的.请考虑以下代码,其中GUI初始化在main方法本身中完成
public class GridBagLayoutTester extends JPanel implements ActionListener {
public GridBagLayoutTester() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("Testing");
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
button.addActionListener(this);
add(button, gbc);
}
public void actionPerformed(ActionEvent e) {
System.out.println("event handler code");
}
public static void main(String[] args) {
JFrame frame = new JFrame("GridBagLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
frame.setSize(800, …Run Code Online (Sandbox Code Playgroud) 我正在Java平台上开发一个实时策略游戏克隆,我有一些关于放置位置和如何管理游戏状态的概念性问题.游戏使用Swing/Java2D作为渲染.在当前的开发阶段,不存在模拟,也不存在AI,只有用户能够改变游戏的状态(例如,建造/拆除建筑物,添加 - 移除生产线,组装车队和设备).因此,可以在事件调度线程中执行游戏状态操作而无需任何渲染查找.游戏状态还用于向用户显示各种聚合信息.
但是,由于我需要引入模拟(例如,构建进度,人口变化,车队运动,制造过程等),在Timer和EDT中更改游戏状态肯定会降低渲染速度.
假设模拟/ AI操作每500毫秒执行一次,我使用SwingWorker计算长度约为250毫秒.如何确保模拟和可能的用户交互之间没有关于游戏状态读取的竞争条件?
我知道模拟的结果(少量数据)可以通过SwingUtilities.invokeLater()调用有效地移回EDT.
游戏状态模型似乎过于复杂,无法在任何地方使用不可变值类.
是否有相对正确的方法来消除这种阅读竞争条件?也许在每个计时器刻度上进行全部/部分游戏状态克隆或将游戏状态的生存空间从EDT改为其他线程?
更新:(根据我给出的评论)游戏使用13个AI控制的玩家,1个人类玩家,并拥有大约10000个游戏对象(行星,建筑物,设备,研究等).例如,游戏对象具有以下属性:
World (Planets, Players, Fleets, ...)
Planet (location, owner, population, type,
map, buildings, taxation, allocation, ...)
Building (location, enabled, energy, worker, health, ...)
在一个场景中,用户在这个星球上建造一座新建筑.这是在EDT中执行的,因为需要更改地图和建筑物集合.与此同时,每隔500毫秒进行一次模拟,以计算所有游戏行星上建筑物的能量分配,这需要遍历建筑物集合以进行统计收集.如果计算分配,则将其提交给EDT并且分配每个建筑物的能量场.
只有人类玩家互动具有此属性,因为AI计算的结果无论如何都应用于EDT中的结构.
通常,75%的对象属性是静态的,仅用于渲染.其余部分可通过用户交互或模拟/ AI决策进行更改.还确保,在前一个步骤写回所有更改之前,不会启动新的模拟/ AI步骤.
我的目标是:
选项:
所有这些都具有模型和游戏的优点,缺点和原因.
更新2:我在谈论这个游戏.我的克隆就在这里.屏幕截图可能有助于想象渲染和数据模型交互.
更新3:
我将尝试给出一个小代码示例以澄清我的问题,因为它似乎从评论中被误解:
List<GameObject> largeListOfGameObjects = ...
List<Building> preFilteredListOfBuildings = ...
// In EDT
public void onAddBuildingClicked() {
Building b = new Building(100 /* kW */);
largeListOfGameObjects.add(b); …Run Code Online (Sandbox Code Playgroud) 我了解了swing如何不是线程安全的.深入研究,我发现对swing组件的每次修改都必须在Event Dispatch Thread上完成,以防止与多线程相关的各种问题.但是,信息似乎完全停止了.似乎没有一个很好的教程可以解释如何在互联网上访问任何地方.
将来自与其他问题相关的代码中的信息修补在一起,似乎我必须在我的程序中的每个swing修改中放置一个不整齐的代码块(就像我自己的代码中的这个例子):
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setTitle("Frame title");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(800, 480);
setLocationRelativeTo(null);
setIconImage(Toolkit.getDefaultToolkit().createImage(ClassLoader.getSystemResource("Frame icon.png")));
}
});
} catch (Exception e) {
e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
基本上,这是对的吗?我是否必须将代码(或与invokeLater等效的代码)放在代码中对Swing组件的每次修改中?
另外,为什么Swing不会自动执行此操作?
我有一个消息标签和一个提交按钮.将多次按下提交按钮,每次按下的操作最多可能需要一分钟.
按下按钮时,我想将消息设置为空,在任务完成后,我想将消息设置为"完成".
private void submitActionPerformed(java.awt.event.ActionEvent evt) {
message = "";
updateMessageLabel();
doTheTask();
/* this update is apply to the label after completion */
message = "Complete";
}
Run Code Online (Sandbox Code Playgroud)
是否可以在submitActionPerformed()方法运行之前(或在方法中)更新该消息标签,但是在单击按钮之后?
我有以下用例:
我在线程A(不是EDT)中执行代码.然后我想问用户一个问题,但这必须在EDT上完成,因为它涉及Swing代码(打开一个对话框等).最后,我想将用户的答案传递给线程A,因此它可以继续.
我很难找到一个好方法将用户的答案传递给线程A.你怎么做到这一点?
该EventQueuejavadoc的规定,则要求入队的事件顺序,为了调度.
难道纠正Runnableš排队,以EventQueue.invokeLater保证后续的用户事件(例如,之前被分派MouseEvent)?换句话说Runnable,如果用户事件发生之后,可以在排队之前执行事件处理程序EventQueue.invokeLater.
谢谢!
我一直在阅读很多关于Swing,线程,invokeLater(),SwingWorker等的内容,但我似乎无法理解这一切,所以我试图创建一个非常简单的程序来说明.我看了很多例子,但似乎没有一个例子表明我正在尝试做什么.
这是我在我的例子中想要做的事情.我有一个按钮和一个标签,当我单击按钮时,我希望程序暂停3秒钟,然后在标签文本中添加句点.在这3秒钟内,我希望GUI正常显示并继续响应额外的点击.这是我写的:
import javax.swing.SwingWorker;
public class NewJFrame extends javax.swing.JFrame
{
private javax.swing.JButton jButton1;
private javax.swing.JLabel jLabel1;
public NewJFrame()
{
initComponents();
}
private void initComponents()
{
jButton1 = new javax.swing.JButton();
jLabel1 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jButton1.setText("Button");
jButton1.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(java.awt.event.ActionEvent evt)
{
jButton1ActionPerformed(evt);
}
});
getContentPane().add(jButton1, java.awt.BorderLayout.CENTER);
jLabel1.setText("Text");
getContentPane().add(jLabel1, java.awt.BorderLayout.PAGE_END);
pack();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)
{
SwingWorker worker=new SwingWorker()
{
protected Object doInBackground()
{
try{Thread.sleep(3000);}
catch (InterruptedException ex){}
return null;
}
};
worker.execute();
jLabel1.setText(jLabel1.getText()+".");
}
public static void …Run Code Online (Sandbox Code Playgroud) 我有几个问题Platform.runLater.我有一个JavaFX Application类.在这个类中,我运行一个线程(该线程从网络套接字读取数据).
现在当我Stage在线程内部创建一个新的时,系统会抛出一个execption(JavaFX事件调度程序线程和我的netork-read线程不一样) - 我理解这种行为.
但另一方面是,我将文本从网络阅读器附加到现有的TextArea或添加/删除一些项目ListView<String>- 这不会引发异常 - 为什么?我认为JavaFX是单线程的(ui库部分).这与Swing中的相同:有时它可以工作,有时你只有垃圾(因为EDT)?
我的问题:
Platform.runLater同一个run()方法?结合try catch(或多次捕获),它看起来很奇怪我知道Platform.runLater在一个线程中的用法并不是那么好(设计解决方案)
我在尝试实现控制台样式组件时遇到了 JTextPane 的性能限制。在大多数情况下,我的控制台运行良好,但是尝试使用大量非空格分隔文本向它发送垃圾邮件最终会完全冻结 GUI。我想避免这种情况,或者至少提供一个以正常方式点击停止按钮的机会。
一些快速分析显示,EDT 大部分时间都被困在 JTextPane 中布置文本(将 LabelViews 作为其 EditorKit 实现的一部分进行布置) - 由于 Swing 的东西应该在 EDT 上完成,我以为我被搞砸了。但随即又是一丝希望。经过一番研究,我偶然发现了一些丢失的摇摆艺术。即,Timothy Prinzing撰写的这篇文章。
这篇(现已完全失效)文章描述了如何将困扰我(布局)的问题从 EDT 中排除,定义了一个名为 的类AsyncBoxView,令我惊讶的是,它现在是 Swing 的一部分。但...
在修改我的编辑器工具包以创建 AsyncBoxView 而不是通常的之后BoxView,我立即遇到了一个障碍——它在初始化期间抛出了一个 NPE。这是一些代码:
package com.stackoverflow
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class ConsoleTest extends JFrame {
public static final boolean USE_ASYNC_BOX_VIEW = true;
public static final int MAX_CHARS = 1000000;
public static final int MAX_LINES = 100;
private static …Run Code Online (Sandbox Code Playgroud) 可能这是微不足道的,我很难理解SwingWorker上的简单文档.
这是复制粘贴的内容
工作流程
SwingWorker的生命周期涉及三个线程:
当前线程:在此线程上调用execute()方法.它安排SwingWorker在工作线程上执行并立即返回.可以等待SwingWorker使用get方法完成.
工作线程:在此线程上调用doInBackground()方法.这是所有背景活动应该发生的地方.要通知PropertyChangeListeners有关绑定属性的更改,请使用firePropertyChange和getPropertyChangeSupport()方法.默认情况下,有两个绑定属性:状态和进度.
事件调度线程:此线程上发生所有与Swing相关的活动.SwingWorker调用process和done()方法并通知此线程上的任何PropertyChangeListeners.
通常,Current线程是Event Dispatch Thread.
-
工作线程不是EDT,因此doInBackground()中的代码不能访问GUI元素.我的理解是否正确?
背景:我们有使用SwingWorker的小代码,但有doInBackground()创建FileChooser并调用setCurrentDirectory().我怀疑这导致我异常几乎与 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6637181(11-Closed,not a defect)相同