1

我有一个扩展 JDialog 的登录表单,用户可以通过刷卡或输入用户名和密码登录。

我创建了一个Runnable与磁条阅读器通信的守护程序,启动时它将请求刷卡,它会等到刷卡。如果应用程序需要取消请求以执行其他操作,那么它将产生该线程将捕获的事件,从而取消等待刷卡的请求。

当用户刷卡时,应用程序将读取用户ID的轨迹并进行验证,如果验证成功,将向刷卡守护进程发送停止命令并停止线程。

当用户输入用户名和密码时,swing 包将访问一个线程 (AWT-EventQueue-0),该线程响应登录按钮的单击事件并继续评估登录凭据。

我的问题是,每当应用程序在此 AWT-EventQueue-0 线程上时,向刷卡守护程序发送停止事件将不起作用,并且该守护程序将保留在线程堆栈上。

编辑1:停止命令在刷卡登录时工作得很好。它优雅地结束了刷卡线程。在这种情况下,当前线程范围在 CardSwipeThread 上。

问题发生在手动登录时,当用户单击登录按钮时,当前作用域线程将是 AWT-EventQueue-0 或事件调度线程。将 CardSwipeThread 的 volatile 布尔值更新为 false 并不会阻止它运行。

编辑2: 读卡器与应用程序通信的唯一时间是刷卡时,问题发生在不需要刷卡的手动登录上。因此,不会因为阻塞的 IO 操作而导致 CardSwipeThread 没有正确结束的问题。原来有一个躲在灌木丛后面。

这是我的代码的一部分:

登录对话框.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;
    }
}
4

2 回答 2

3

考虑SwingWorker<Boolean, Boolean>在这种情况下使用。execute()在显示登录对话框之前,您需要工作人员。如果以及何时可用,请在您的实现doInBackground()和结果中轮询读者。如果读者身份验证成功,则在事件调度线程publish()上执行的 的实现可以关闭对话框。因为implements ,如果键盘登录成功,你就可以工作了。有关其他示例,请参阅Worker Threads 和 SwingWorker以及标签。process()SwingWorkerFuturecancel()

这将需要我对代码进行一些返工。

我认为值得付出努力,但是在过于激进地重构之前,您需要运行一些示例。要查看数据流,请尝试这个从伪设备读取的示例。要查看其工作原理cancel(),请尝试运行任意.Process

于 2017-08-23T06:37:35.490 回答
1

如果您的刷卡器在刷卡后停止,但在手动登录后没有停止,那么它正在等待阻塞io读取卡,它甚至没有完成一个循环运行!它永远等待数据,所以甚至无法检查是否listenforswipe设置为 false!

您不应该停止读卡线程,而是根据应用程序的状态对从读卡线程读取卡数据的事件采取适当的措施。

这将解决阻塞 IO 的问题,只需让读卡线程等待接收数据,并在读取数据的情况下根据您的应用程序状态将其发送到正确的目标。就像使用键盘一样-您无需启动和停止键盘-您只需更改焦点,因此键盘的输入会根据桌面的当前状态转到不同的目标。

我之前的回答:

您的代码的其他部分有一些问题,当您调用停止时,所有 CardSwipeThread 实例都应该完成(因为listenforswipeis private static volatile boolean),它没有停止的可能原因有两个:

  1. stop() 方法永远不会被调用 - 请调试它是否被调用

  2. " // Code for reading the magnetic stripe data." 没有结束,可能会阻塞某些 IO 操作 - 等待读取数据。如果是这种情况,那么如果您第二次刷卡,它可能会完成 CardSwipeThread - 但这取决于您如何实现此代码,请发布此代码,以便我们为您提供帮助

此外,如果您打算让其他人登录/注销,那么为什么要停止 CardSwipeThread 守护程序?让它运行,当有人已经登录时正确处理读卡事件,那会发生什么?这样,您无需担心 CardSwipeThread 正在等待阻塞 IO 以等待某人进入卡片。

编辑:

使用的想法在SwingWorker这里并不能解决任何问题,如果您调用该cancel方法,它无论如何都会等待读取数据。这可能导致由于资源处理不当而无法启动new SwingWorkerwith的情况。CardSwipeReader这可能导致读卡器无法使用。这种 SwingWorker 方法甚至没有触及读卡代码中 BlockingIO 的真正问题。

于 2017-08-23T06:29:13.370 回答