从事件调度线程 (EventQueue) 停止另一个线程
Stopping another thread from an Event Dispatch Thread (EventQueue)
我有一个扩展 JDialog 的登录表单,用户可以通过刷卡或输入用户名和密码登录。
我创建了一个 Runnable
守护程序,它与磁条 reader 通信,启动时它会请求刷卡,它会一直等到有人刷卡。如果应用程序需要取消执行其他操作的请求,那么它将产生一个事件,该线程将捕获该事件,从而取消等待刷卡的请求。
当用户刷卡时,应用程序将读取用户 ID 的轨道并验证它,如果身份验证成功,将向刷卡守护进程发送停止命令并停止线程。
当用户输入用户名和密码时,swing 包将访问一个线程 (AWT-EventQueue-0),该线程响应登录按钮的单击事件并继续评估登录凭据。
我的问题是每当应用程序在此 AWT-EventQueue-0 线程上时,向刷卡守护程序发送停止事件将不起作用,守护程序将停留在线程堆栈中。
编辑 1: 停止命令在刷卡登录时运行良好。它优雅地结束刷卡线程。在这种情况下,当前线程范围在 CardSwipeThread 上。
问题发生在手动登录上,当用户单击登录按钮时,当前作用域线程将是 AWT-EventQueue-0 或事件调度线程。将 CardSwipeThread 的可变布尔值更新为 false 不会阻止它从 运行.
编辑 2: reader 唯一一次与应用程序通信是在刷卡时,问题发生在不需要刷卡的手动登录。因此,CardSwipeThread 不会因为 IO 操作受阻而无法正确结束。 原来有一个隐藏在灌木丛后面。
这是我的代码的一部分:
LoginDialog.java
public class LoginDialog extends JDialog implements ActionListener, WindowListener
{
public LoginDialog()
{
super();
// ..More code that instantiates the objects of this JDialog.
SwipeReader.getInstance().enable(true);
}
class SymAction implements java.awt.event.ActionListener
{
public void actionPerformed(java.awt.event.ActionEvent event)
{
Object object = event.getSource();
if (object == logonButton)
{
logonButton_actionPerformed(event);
}
// ..More conditions for other objects.
}
}
// The keyboard login method, does not terminate the card swipe daemon thread.
void logonButton_actionPerformed(java.awt.event.ActionEvent event)
{
try
{
// ..More code that will evaluate login information.
if (authenticate == -1)
{
// Notify for failed login.
}
else if (authenticate == 0)
{
SwipeReader.getInstance().enable(false);
}
}
catch (Exception e)
{
// Error logger.
}
}
// The card swipe listener used for card login.
public void deviceDataReceived(Object object)
{
try
{
// ..More code that will evaluate login information.
if (authenticate == -1)
{
// Notify for failed login.
}
if (authenticate == 0)
{
SwipeReader.getInstance().enable(false);
}
}
catch (Exception e)
{
// Error logger.
}
}
}
SwipeReader.java
public class SwipeReader
{
// This is a singleton class that creates the thread for the daemon.
CardSwipeDaemon cardSwipeDaemon;
Thread cardSwipeThread;
SwipeReader instance;
private SwipeReader() {}
public static SwipeReader getInstance()
{
if (instance == null) { instance = new SwipeReader(); }
return instance;
}
public void enable (boolean isEnabled)
{
if (isEnabled)
{
cardSwipeDaemon = new CardSwipeDaemon();
cardSwipeThread = new Thread(cardSwipeDaemon, "CardSwipeThread");
cardSwipeThread.start();
}
else
{
cardSwipeDaemon.stop();
cardSwipeThread = null;
}
}
}
CardSwipeDaemon.java
public class CardSwipeDaemon implements Runnable
{
CardSwipeDaemon instance;
private static volatile boolean listenforswipe = true;
public CardSwipeDaemon() {}
public static synchronized CardSwipeDaemon getInstance()
{
if (instance == null) { instance = new CardSwipeDaemon(); }
return instance;
}
public run()
{
listenforswipe = true;
while (listenforswipe)
{
// Code for reading the magnetic stripe data.
}
}
public void stop()
{
listenforswipe = false;
}
}
If your card swipe reader stops after card login, but doesn't stop after manual login, then it is waiting on blocking io to read the
card, it doesn't even finish one loop run! It waits for the data
forever, so can't even check that the listenforswipe
was set to
false!
您不应该停止读卡线程,而是根据您的应用程序的状态对从读卡线程读取卡数据的事件采取适当的操作。
这将解决阻塞 IO 的问题,只需让读卡线程等待接收数据,并在读取数据时根据您的应用程序状态将其发送到适当的目标。就像使用键盘一样 - 您不需要启动和停止键盘 - 您只需更改焦点,因此来自键盘的输入会根据桌面的当前状态转到不同的目标。
我之前的回答:
你的代码的其他部分有问题,当你调用停止时 CardSwipeThread 的所有实例都应该完成(因为 listenforswipe
是 private static volatile boolean
),有两个可能的原因不停:
从未调用过 stop() 方法 - 请调试它已被调用
“// Code for reading the magnetic stripe data.
”没有结束,可能会阻塞某些 IO 操作 - 等待读取数据。如果是这种情况,那么如果您第二次刷卡,它可能会完成 CardSwipeThread - 但这取决于您如何实现此代码,请post此代码,以便我们可以帮助您
此外,如果您打算让其他人 login/unlogin 那么为什么要停止 CardSwipeThread 守护程序?让它运行,在有人已经登录的情况下正常服务读卡事件,那会发生什么?这样,您就不必担心 CardSwipeThread 正在等待阻塞 IO 以等待某人进入卡片。
编辑:
使用SwingWorker
的想法并不能解决这里的任何问题,如果你调用cancel
方法,它无论如何都会等待读取数据。这可能会导致由于未正确处理资源而无法启动带有 CardSwipeReader
的新 SwingWorker
的情况。这可能会导致卡 reader 无法使用。 这种SwingWorker的做法甚至没有触及读卡代码中BlockingIO的真正问题
考虑使用 SwingWorker<Boolean, Boolean>
in this context. You would execute()
the worker before displaying the login dialog. Poll the reader in your implementation of doInBackground()
and publish()
the result if and when it becomes available. Your implementation of process()
, which executes on the event dispatch thread, can close the dialog if reader authentication succeeds. Because SwingWorker
implements Future
, you can cancel()
the worker if keyboard login succeeds. See Worker Threads and SwingWorker and the swingworker 标签作为其他示例。
This would require me a bit of rework on the code.
我认为这是值得的,但您需要 运行 一些示例,然后再进行过于激进的重构。要查看数据流,请尝试 example that reads from a pseudo-device. To see how cancel()
works, try this example 运行 任意 Process
。
我有一个扩展 JDialog 的登录表单,用户可以通过刷卡或输入用户名和密码登录。
我创建了一个 Runnable
守护程序,它与磁条 reader 通信,启动时它会请求刷卡,它会一直等到有人刷卡。如果应用程序需要取消执行其他操作的请求,那么它将产生一个事件,该线程将捕获该事件,从而取消等待刷卡的请求。
当用户刷卡时,应用程序将读取用户 ID 的轨道并验证它,如果身份验证成功,将向刷卡守护进程发送停止命令并停止线程。
当用户输入用户名和密码时,swing 包将访问一个线程 (AWT-EventQueue-0),该线程响应登录按钮的单击事件并继续评估登录凭据。
我的问题是每当应用程序在此 AWT-EventQueue-0 线程上时,向刷卡守护程序发送停止事件将不起作用,守护程序将停留在线程堆栈中。
编辑 1: 停止命令在刷卡登录时运行良好。它优雅地结束刷卡线程。在这种情况下,当前线程范围在 CardSwipeThread 上。
问题发生在手动登录上,当用户单击登录按钮时,当前作用域线程将是 AWT-EventQueue-0 或事件调度线程。将 CardSwipeThread 的可变布尔值更新为 false 不会阻止它从 运行.
编辑 2: reader 唯一一次与应用程序通信是在刷卡时,问题发生在不需要刷卡的手动登录。因此,CardSwipeThread 不会因为 IO 操作受阻而无法正确结束。 原来有一个隐藏在灌木丛后面。
这是我的代码的一部分:
LoginDialog.java
public class LoginDialog extends JDialog implements ActionListener, WindowListener
{
public LoginDialog()
{
super();
// ..More code that instantiates the objects of this JDialog.
SwipeReader.getInstance().enable(true);
}
class SymAction implements java.awt.event.ActionListener
{
public void actionPerformed(java.awt.event.ActionEvent event)
{
Object object = event.getSource();
if (object == logonButton)
{
logonButton_actionPerformed(event);
}
// ..More conditions for other objects.
}
}
// The keyboard login method, does not terminate the card swipe daemon thread.
void logonButton_actionPerformed(java.awt.event.ActionEvent event)
{
try
{
// ..More code that will evaluate login information.
if (authenticate == -1)
{
// Notify for failed login.
}
else if (authenticate == 0)
{
SwipeReader.getInstance().enable(false);
}
}
catch (Exception e)
{
// Error logger.
}
}
// The card swipe listener used for card login.
public void deviceDataReceived(Object object)
{
try
{
// ..More code that will evaluate login information.
if (authenticate == -1)
{
// Notify for failed login.
}
if (authenticate == 0)
{
SwipeReader.getInstance().enable(false);
}
}
catch (Exception e)
{
// Error logger.
}
}
}
SwipeReader.java
public class SwipeReader
{
// This is a singleton class that creates the thread for the daemon.
CardSwipeDaemon cardSwipeDaemon;
Thread cardSwipeThread;
SwipeReader instance;
private SwipeReader() {}
public static SwipeReader getInstance()
{
if (instance == null) { instance = new SwipeReader(); }
return instance;
}
public void enable (boolean isEnabled)
{
if (isEnabled)
{
cardSwipeDaemon = new CardSwipeDaemon();
cardSwipeThread = new Thread(cardSwipeDaemon, "CardSwipeThread");
cardSwipeThread.start();
}
else
{
cardSwipeDaemon.stop();
cardSwipeThread = null;
}
}
}
CardSwipeDaemon.java
public class CardSwipeDaemon implements Runnable
{
CardSwipeDaemon instance;
private static volatile boolean listenforswipe = true;
public CardSwipeDaemon() {}
public static synchronized CardSwipeDaemon getInstance()
{
if (instance == null) { instance = new CardSwipeDaemon(); }
return instance;
}
public run()
{
listenforswipe = true;
while (listenforswipe)
{
// Code for reading the magnetic stripe data.
}
}
public void stop()
{
listenforswipe = false;
}
}
If your card swipe reader stops after card login, but doesn't stop after manual login, then it is waiting on blocking io to read the card, it doesn't even finish one loop run! It waits for the data forever, so can't even check that the
listenforswipe
was set to false!
您不应该停止读卡线程,而是根据您的应用程序的状态对从读卡线程读取卡数据的事件采取适当的操作。
这将解决阻塞 IO 的问题,只需让读卡线程等待接收数据,并在读取数据时根据您的应用程序状态将其发送到适当的目标。就像使用键盘一样 - 您不需要启动和停止键盘 - 您只需更改焦点,因此来自键盘的输入会根据桌面的当前状态转到不同的目标。
我之前的回答:
你的代码的其他部分有问题,当你调用停止时 CardSwipeThread 的所有实例都应该完成(因为 listenforswipe
是 private static volatile boolean
),有两个可能的原因不停:
从未调用过 stop() 方法 - 请调试它已被调用
“
// Code for reading the magnetic stripe data.
”没有结束,可能会阻塞某些 IO 操作 - 等待读取数据。如果是这种情况,那么如果您第二次刷卡,它可能会完成 CardSwipeThread - 但这取决于您如何实现此代码,请post此代码,以便我们可以帮助您
此外,如果您打算让其他人 login/unlogin 那么为什么要停止 CardSwipeThread 守护程序?让它运行,在有人已经登录的情况下正常服务读卡事件,那会发生什么?这样,您就不必担心 CardSwipeThread 正在等待阻塞 IO 以等待某人进入卡片。
编辑:
使用SwingWorker
的想法并不能解决这里的任何问题,如果你调用cancel
方法,它无论如何都会等待读取数据。这可能会导致由于未正确处理资源而无法启动带有 CardSwipeReader
的新 SwingWorker
的情况。这可能会导致卡 reader 无法使用。 这种SwingWorker的做法甚至没有触及读卡代码中BlockingIO的真正问题
考虑使用 SwingWorker<Boolean, Boolean>
in this context. You would execute()
the worker before displaying the login dialog. Poll the reader in your implementation of doInBackground()
and publish()
the result if and when it becomes available. Your implementation of process()
, which executes on the event dispatch thread, can close the dialog if reader authentication succeeds. Because SwingWorker
implements Future
, you can cancel()
the worker if keyboard login succeeds. See Worker Threads and SwingWorker and the swingworker 标签作为其他示例。
This would require me a bit of rework on the code.
我认为这是值得的,但您需要 运行 一些示例,然后再进行过于激进的重构。要查看数据流,请尝试 example that reads from a pseudo-device. To see how cancel()
works, try this example 运行 任意 Process
。