Java awt EventListener 是异步的吗?
Java awt EventListener asynchronous?
我正在开发 Java awt 应用程序。我们目前有一个 class 实现 runnable 并为我们应用程序中的对象调用渲染。我们还有键盘和鼠标侦听器,可以调用各种对象上的函数。我注意到当快速按下多个键时会出现一个奇怪的错误,经过一些调查后,事件侦听器似乎与正在执行渲染的主线程分开异步调用。谁能确认 Java awt 事件侦听器是异步调用的,并提出可能的解决方案?
public class Driver extends Canvas implements Runnable
{
private boolean running = false;
private Integer frames;
private Thread thread;
private Window window;
private Mouse mouse;
private Keyboard keyboard;
/**
* Constructor of Driver
* Initiates Window, Mouse, Keyboard, handlers
*/
public Driver()
{
window = new Window("Dominion", this);
mouse = new Mouse(this);
keyboard = new Keyboard(this);
}
/**
* updates classes/variables that change per frame
*/
public void tick() {
//Framerate independent calls
}
/**
* Draws the game onto the window
* Calls other handler render to draw their parts
*/
public void render()
{
BufferStrategy bs = this.getBufferStrategy();
if (bs == null)
{
this.createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
//Start Graphics
g.setColor(Color.WHITE);
g.fillRect(0, 0, window.getWidth(), window.getHeight());
//Rest of rendering
//End Graphics
g.dispose();
bs.show();
}
/**
* Starts thread
*/
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
/**
* Stops thread
*/
public synchronized void stop() {
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Important game function that calls the render and tick methods
*/
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
/**
* getter for window
* @return window
*/
public Window getWindow()
{
return window;
}
/**
* Starts up the whole Client side of things
*/
public static void main(String[] args)
{
new Driver();
}
}
和鼠标
public class Mouse implements MouseListener, MouseMotionListener, MouseWheelListener{
private Driver d;
/**
* Creates a Mouse object
* @param driver
*/
public Mouse(Driver driver) {
this.d = driver;
d.addMouseListener(this);
d.addMouseMotionListener(this);
d.addMouseWheelListener(this);
}
/**
* Invoked when the mouse button has been clicked (pressed
* and released) on a component.
* @param e
*/
@Override
public void mouseClicked(MouseEvent e) {
}
//Other methods cut out
}
Swing 是严格的单线程,Swing 所做的一切都发生在它自己的线程(事件调度线程,又名 EDT)上。这不是您的 java 程序启动的主线程。很少有 Swing 方法可以从 EDT 之外安全地调用(遗憾的是 java 文档对此不是很明确)。
大多数 java GUI 框架具有相同的限制(例如 JavaFX 使用非常相似的方法)。
因此,您附加到 Swing 组件的任何侦听器实际上都会在 EDT 上执行 运行。就 Component 的 paint() 方法而言,绘画也发生在 EDT 上。
根据应用程序的要求,有许多方法可以在满足 Swings 线程要求的同时满足应用程序要求。
最简单的情况是完全由用户输入驱动的应用程序(在这种情况下,您只需创建一个 GUI,一切都会响应用户引起的事件,例如鼠标点击或按键)。由于侦听器中的所有代码 运行,它由 EDT 的 Swing 自动调用,不会发生线程问题。缺点是您不能执行长 运行ning 方法,因为它们会阻止 EDT,导致 GUI 无响应。
为了解决无响应的 GUI 问题,长时间的 运行ning 工作被安排在 EDT 之外,例如使用 SwingWorker 实用程序 class。这种方法适用于数据库访问、文件 I/O 或响应用户输入而完成的一般计算。
对于持续更新 GUI 的游戏,上述方法是不够的。对于简单的游戏,EDT 上的一切都可以 运行,例如SwingTimer 获取常规 "ticks",其中 Swing 从 EDT 调用应用程序代码。 运行 这样的所有逻辑都避免了线程问题。
对于还需要处理异步任务的成熟游戏,例如网络、资源流等,多线程方法更适合。这使事情变得更加复杂,因为应用程序需要确保数据对象是从例如GUI 的加载程序线程已正确同步,并且一次不会被多个线程修改。游戏状态也是如此,当游戏状态被渲染时,它不能被修改。如果游戏主线程和渲染需要异步(例如实时游戏逻辑如果渲染慢就不能等待),主游戏线程可能需要制作快照副本以传递游戏状态进行渲染。
这只是您可以采取的方式的粗略概述:)
我正在开发 Java awt 应用程序。我们目前有一个 class 实现 runnable 并为我们应用程序中的对象调用渲染。我们还有键盘和鼠标侦听器,可以调用各种对象上的函数。我注意到当快速按下多个键时会出现一个奇怪的错误,经过一些调查后,事件侦听器似乎与正在执行渲染的主线程分开异步调用。谁能确认 Java awt 事件侦听器是异步调用的,并提出可能的解决方案?
public class Driver extends Canvas implements Runnable
{
private boolean running = false;
private Integer frames;
private Thread thread;
private Window window;
private Mouse mouse;
private Keyboard keyboard;
/**
* Constructor of Driver
* Initiates Window, Mouse, Keyboard, handlers
*/
public Driver()
{
window = new Window("Dominion", this);
mouse = new Mouse(this);
keyboard = new Keyboard(this);
}
/**
* updates classes/variables that change per frame
*/
public void tick() {
//Framerate independent calls
}
/**
* Draws the game onto the window
* Calls other handler render to draw their parts
*/
public void render()
{
BufferStrategy bs = this.getBufferStrategy();
if (bs == null)
{
this.createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
//Start Graphics
g.setColor(Color.WHITE);
g.fillRect(0, 0, window.getWidth(), window.getHeight());
//Rest of rendering
//End Graphics
g.dispose();
bs.show();
}
/**
* Starts thread
*/
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
/**
* Stops thread
*/
public synchronized void stop() {
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Important game function that calls the render and tick methods
*/
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
/**
* getter for window
* @return window
*/
public Window getWindow()
{
return window;
}
/**
* Starts up the whole Client side of things
*/
public static void main(String[] args)
{
new Driver();
}
}
和鼠标
public class Mouse implements MouseListener, MouseMotionListener, MouseWheelListener{
private Driver d;
/**
* Creates a Mouse object
* @param driver
*/
public Mouse(Driver driver) {
this.d = driver;
d.addMouseListener(this);
d.addMouseMotionListener(this);
d.addMouseWheelListener(this);
}
/**
* Invoked when the mouse button has been clicked (pressed
* and released) on a component.
* @param e
*/
@Override
public void mouseClicked(MouseEvent e) {
}
//Other methods cut out
}
Swing 是严格的单线程,Swing 所做的一切都发生在它自己的线程(事件调度线程,又名 EDT)上。这不是您的 java 程序启动的主线程。很少有 Swing 方法可以从 EDT 之外安全地调用(遗憾的是 java 文档对此不是很明确)。
大多数 java GUI 框架具有相同的限制(例如 JavaFX 使用非常相似的方法)。
因此,您附加到 Swing 组件的任何侦听器实际上都会在 EDT 上执行 运行。就 Component 的 paint() 方法而言,绘画也发生在 EDT 上。
根据应用程序的要求,有许多方法可以在满足 Swings 线程要求的同时满足应用程序要求。
最简单的情况是完全由用户输入驱动的应用程序(在这种情况下,您只需创建一个 GUI,一切都会响应用户引起的事件,例如鼠标点击或按键)。由于侦听器中的所有代码 运行,它由 EDT 的 Swing 自动调用,不会发生线程问题。缺点是您不能执行长 运行ning 方法,因为它们会阻止 EDT,导致 GUI 无响应。
为了解决无响应的 GUI 问题,长时间的 运行ning 工作被安排在 EDT 之外,例如使用 SwingWorker 实用程序 class。这种方法适用于数据库访问、文件 I/O 或响应用户输入而完成的一般计算。
对于持续更新 GUI 的游戏,上述方法是不够的。对于简单的游戏,EDT 上的一切都可以 运行,例如SwingTimer 获取常规 "ticks",其中 Swing 从 EDT 调用应用程序代码。 运行 这样的所有逻辑都避免了线程问题。
对于还需要处理异步任务的成熟游戏,例如网络、资源流等,多线程方法更适合。这使事情变得更加复杂,因为应用程序需要确保数据对象是从例如GUI 的加载程序线程已正确同步,并且一次不会被多个线程修改。游戏状态也是如此,当游戏状态被渲染时,它不能被修改。如果游戏主线程和渲染需要异步(例如实时游戏逻辑如果渲染慢就不能等待),主游戏线程可能需要制作快照副本以传递游戏状态进行渲染。
这只是您可以采取的方式的粗略概述:)